JDBC(Java DataBase Connectivity)时用于运行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂商则对接口进行实现,这样开发人员就可以无需接触底层数据库驱动程序的差异性。
JDBC简介
厂商在实现JDBC驱动程序时,依方式可将驱动程序分为四种类型:
- JDBC-ODBC Bridge Driver
将JDBC调用转换为ODBC调用 - Native API Driver
将JDBC调用转换为原生链接库中的相关API调用。特点:与平台相依,是四种类型中最快的驱动程序。 - JDBC-Net Driver
将JDBC调用转换为特定的网络协议调用,目的是与远程数据库特定的中间服务器或组件进行协议操作,而中间件或组建再真正与数据库进行操作。特点:弹性最高。 - Native Protocol Driver
主要作用是将JDBC调用转换为特定的网络协议,所以驱动驱动程序可以使用Java技术来实现,遗传你这类型的驱动程序可以跨平台,在性能上也有不错的表现。在不需要如第3种驱动类型的灵活性时,通常是会使用这类型驱动程序,该类型时最常见的驱动程序类型。
以下编写一个简单的JavaBean来测试可否连接数据库:
package club.chuxing;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DbBean {
private String url;
private String username;
private String password;
public DbBean(){
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
Logger.getLogger(DbBean.class.getName()).log(Level.SEVERE, null, e);
}
}
public boolean isConnectedOk() {
boolean ok = false;
Connection conn = null;
try {
conn = DriverManager.getConnection(url, username, password);
if (!conn.isClosed()) {
ok = true;
}
} catch (SQLException e) {
Logger.getLogger(DbBean.class.getName()).log(Level.SEVERE, null, e);
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
Logger.getLogger(DbBean.class.getName())
.log(Level.SEVERE, null, e);
}
}
}
return ok;
}
public void setPassword(String password) {
this.password = password;
}
public void setUrl(String url) {
this.url = url;
}
public void setName(String username) {
this.username = username;
}
}
之后就可以通过调用isConnectedOK()方法来看看是否可以连接成功。例如,可以写个简单的JSP网页,代码如下:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:useBean id="db" class="club.chuxing.DbBean" />
<c:set target="${db}" property="url" value="jdbc:mysql://localhost:3306/test" />
<c:set target="${db}" property="username" value="root" />
<c:set target="${db}" property="password" value="123" />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Test Database Connection</title>
</head>
<body>
<c:choose>
<c:when test="${db.connectedOK}">连接成功</c:when>
<%-- <c:when test="<%db.isConnectedOK();%>">连接成功</c:when> --%>
<c:otherwise>连接失败</c:otherwise>
</c:choose>
</body>
</html>
使用DataSource取得连接
在Java EE中的环境中,将取得连接等与数据库来源相关的行为规范在javax.sql.DataSource
接口,实际如何取得Connection则由实现接口的对象来负责。
为了让应用程序在需要取得某些与系统相关的资源对象时,能与实际的系统配置、实体机器位置、环境架构等无关,在Java应用程序中可以通过JNDI(Java Naming Directory Interface)来取得所需的资源对象。可用如下方式取得DataSource实例:
try {
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
dataSource = (DataSource)envContext.lookup("jdbc/test");
} catch(NamingException ex) {
//other process
}
在创建Context对象的过程中会收集环境的相关数据,之后根据JNDI名称jdbc/demo向JNDI服务器查找DataSource实例并返回。在这个程序片段中,不会知道实际的资源配置、实体机器位置、环境架构等信息,应用程序不会与这些相依。
之前的DbBean类可以改为如下编写方式:
package club.chuxing;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
public class DatabaseBean {
private DataSource dataSource;
public DatabaseBean(){
try {
Context initContext = new InitialContext();
Context envContext = (Context)initContext.lookup("java:/comp/env");
dataSource = (DataSource)envContext.lookup("jdbc/test");
} catch(NamingException ex) {
Logger.getLogger(DatabaseBean.class.getName())
.log(Level.SEVERE, null, ex);
}
}
public boolean isConnectedOK(){
boolean ok = false;
Connection conn = null;
try {
conn = dataSource.getConnection();
if (!conn.isClosed()) {
ok = true;
}
} catch(SQLException ex) {
Logger.getLogger(DatabaseBean.class.getName())
.log(Level.SEVERE, null, ex);
} finally {
if (conn != null) {
try {
conn.close();
} catch(SQLException ex) {
Logger.getLogger(DatabaseBean.class.getName())
.log(Level.SEVERE, null, ex);
}
}
}
return ok;
}
}
以上代码并不含有驱动程序、数据库用户名、密码等信息,这些都由数据库管理人员或服务器管理人员负责设置,我们唯一需要知道的就是jdbc/test这个JNDI名称,并且告诉web容器,也就是要在web.xml中设置。
web.xml:
<web-app ...>
<resource-ref>
<res-ref-name>jdbc/demo</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
<res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
</web-app>
在web.xml中设置的目的,就是让web容器提供JNDI查找时所需的相关环境信息,这样创建Context对象时就不用设置一大堆参数了。接着可以编写一个简单的JSP来使用DatabaseBean。
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<jsp:useBean id="db" class="club.chuxing.DatabaseBean" />
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Test Database Connection</title>
</head>
<body>
<c:choose>
<c:when test="${db.connectedOK}">连接成功</c:when>
<%-- <c:when test="<%= db.isConnectedOK()%>">连接成功</c:when> --%>
<c:otherwise>连接失败</c:otherwise>
</c:choose>
</body>
</html>
对于Java开发人员来说,它的工作已经完成了。现在假设你是服务器管理员,职责就是设置JNDI相关资源,但设置的方式并非标准的一部分,而是依应用程序服务器而有所不同。假设应用程序将部署在Tomcat6上,则可以要求web应用程序在封装为war文件时,必须在META-INF文件夹中包括一个context.xml文件。例如:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/JDBCDemo">
<Resource name="jdbc/demo" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="1000" username="root" password="123"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8"/>
</Context>
name属性用来设置JNDI名称,username与password用来设置数据库用户名和密码,driverClassName用来设置驱动程序类名称,url用来设置JDBC URL。其他属性则是与DBCP(Database Connection Pool)有关的,这是内置在Tomcat中的连接池机制。
当应用程序部署后,Tomcat会根据META-INF中context.xml的设置,寻找指定的驱动程序,所以必须将驱动程序的JAR文件放置在Tomcat的lib目录中,接着Tomcat就会为JNDI名称jdbc/demo设置相关的资源。
使用root来操作数据库是不安全的,root账号用于操作数据库的完整权限,应用设置具有适当权限的用户来操作数据库。