第八章 访问数据库
8.1 安装和配置MySQL数据库
http://www.mysql.com下载mysql的安装软件。
mysql安装后,创建books表。
drop database BookDB;
create database BookDB;
use BookDB;
create table BOOKS(
ID varchar(8) primary key,
NAME varchar(24),
TITLE varchar(96),
PRICE float,
YR int,
DESCRIPTION varchar(128),
SALE_AMOUNT int);
insert into BOOKS values('201', '孙卫琴',
'Java面向对象编程',
65, 2006, '让读者由浅入深掌握Java语言', 20000);
insert into BOOKS values('202', '孙卫琴',
'精通Struts', 49,
2004, '真的很棒', 80000);
insert into BOOKS values('203', '孙卫琴',
'Tomcat与JavaWeb开发技术详解',
45, 2004, '关于Tomcat与JavaWeb开发的最畅销的技术书', 40000);
insert into BOOKS values('204', '孙卫琴',
'Java网络编程精解',
55, 2007, '很值得一看', 20000);
insert into BOOKS values('205', '孙卫琴',
'精通Hibernate',
59, 2005, '权威的Hibernate技术资料', 50000);
insert into BOOKS values('206', '孙卫琴',
'Java2认证考试指南与试题解析',
88, 2002, '权威的Java技术资料', 8000);
8.2 JDBC 简介
JDBC 是 Java DataBase Connectivity的缩写。JDBC是连接java程序和数据库的纽带。JDBC的实现封装了与各种数据库服务器通信的细节。java程序通过JDBC API来访问数据库。有一下有点:
(1)简化访问数据库的程序代码,无需涉及与数据库服务器通信的细节。
(2)不依赖任何数据库平台。同一个java程序可以访问等多种数据库服务。
JDBC 的实现包括三部分:
- JDBC 驱动管理器:java.sql.DriverManger类,由 Oracle 公司实现,负责注册特定JDBC驱动器,以及根据特定驱动器建立与数据库连接。
- JDBC 驱动器API:由 Oracle公司制定,其最主要的接口是java.Driver。
- JDBC 驱动器:由数据库供应商或其他第三方工具提供商创建, 也称为JDBC驱动程序。JDBC驱动器实现了JDBC驱动器 API,负责与特定的数据库连接。以及处理通信细节。JDBC驱动器可以注册到JDBC驱动管理器中。
Oracle公式制定了两套API:
- JDBC API:java应用程序通过它来访问各种数据库。
- JDBC 驱动器API:当数据库供应商或者其他第三方工具提供商为特定数据库创建JDBC驱动器时,该驱动器必须实现JDBC驱动器API。
从上图可以看出,JDBC 驱动才是连接java应用程序和特定数据库的纽带。
当Java 应用程序希望访某种数据库时,要先想DriverManager类注册该数据库的驱动器类,这样,DriverManager 类就能委派该驱动器来执行由应用程序下达的操作数据库的各种任务了。
8.2.1 java.sql包中的接口和类
JDBC API 主要位于java.sql 包中,关键的接口和类包括:
- Driver 接口和 DriverManager 类:前者表示驱动器,后者表示驱动管理器。
- Connection 接口:表示数据库连接。
- Statement 接口:负责执行 SQL 语句。
- PrepareStatement 接口:负责执行预准备的 SQL。
- CallableStatement接口:负责执行 SQL 存储过程。
- ResultSet接口:表示 SQL 查询语句返回的结果集。
1、Driver 接口和 DriverManager 类
所有JDBC驱动都必须实现Driver接口,JDBC 驱动器由数据库厂商或第三方提供,在编写访问数据库的 java 程序时,必须把特定数据库的 JDBC 驱动器的类库加入到classpath中。
DriverManager 类用来建立和数据库的连接以及管理JDBC驱动器。方法都是静态的,主要包括:
-
registerDriver(Driver driver) : 在DriverManager 中注册JDBC驱动。
-
deregisterDriver(Driver driver):在DriverManager 中注销JDBC驱动器。
-
getConnection(String url, String user, String password):建立和数据库的连接,并返回表示数据库连接的Connection对象。
-
setLoginTimeOut(int seconds):设定等待建立数据库连接的超时时间。
-
setLogWriter(PrintWriter out):设定输出JDBC日志的PrintWriter对象。
2、Connection 接口
Connection 接口代表Java程序和数据库的连接,Connection 接口主要包括以下方法:
- getMetaData():返回表示数据库的元数据的DatabaseMetaData对象。元数据包含了描述数据库的相关信息,如数据库中表的结构就是一种元数据。
- createStatement():创建并返回Satement对象。
- prepareStatement():创建并返回PreparedStatement对象。
3、Statement 接口
Statement 接口提供了三个执行SQL语句的方法:
- execute(String sql):执行各种SQL语句。该方法返回一个boolean类型的值,如果为true,表示所执行的SQL语句具有查询结果,可通过Statement的getResultSet()方法获得这一查询结果。
- excuteUpadte(String sql):执行sql的insert、update、delete语句,返回一个int值,表示数据库中受到这条SQL影响的记录数。
- executeQuery(String sql):执行SQL的select语句。返回ResultSet对象。
4、PrepareStatement 接口
PrepareStatement接口继承了 Statement 接口,用来执行预准备的SQL语句。
PrepareStatement的使用步骤:
(1)通过Connection对象的prepareStatement()方法生成PrepareStatement
对象。SQL 语句中用 “?” 代表参数值。
String sql = "select ID,NAME, TITLE,PRICE from books where NAME=? adn PRICE=?";
//预准备sql语句
PreparedStatement prepStmt = connection.prepareStatement(sql);
(2)调用PrepareStatement的setXXX方法给参数赋值:
prepStmt.setString(1, "Tom"); //替换SQL中的第一个?
prepStmt.setFloa(2, 40); //第二个?
(3)执行SQL:
ReusltSet rs = prepStmt.excuteQuery();
5、ResultSet
ResultSet接口表示 select 查询语句得到的结果集。调用ResultSet的next()方法,可以使游标定位到结果集的下一条记录。getXXX()方法,获取字段的值。
如:getString(int columnIndex) 或 getString(String columnName);
8.2.2 编写访问数据库的步骤
- 获得要访问 数据库的 JDBC驱动。
- 在程序中加载并注册 JDBC 驱动。
//加载mysql的jdbc驱动
Class.forName("com.mysql.jdbc.Driver");
//注册驱动,对于Mysql来说,在加载Driver类时,会自动创建本身的实例并调用
//DriverManager.registerDriver()方法注册自身,所以可以省略。
DriverManager.registerDriver(new Driver());
- 建立与数据库的连接
Connection con = DriverManager.getConnection(url, user, password);
url的一般形式为:
jdbc:drivertype://parameters
jdbc:drivertype:driversubtyupe://parameters
drivertype:驱动类型,如mysql,oracle等
driversubtyupe:驱动子类型,是一个可选的参数。
如JDBC-ODBC Driver连接数据库时:
jdbc:odbc:datasource
- 创建Statement 对象,准备执行SQL语句。
Statement stmt = con.createStatement();
- 执行SQL。
String sql = "SELECT ID, NAME FROM books WHERE PRICE = 40";
ResultSet rs = stmt.excuteQuery(sql);
- 访问ResultSet中的记录
while (rs.next()) {
String id = rs.getString("ID");
.......
}
- 依次关闭 ResultSet、Statement、Connection对象。
rs.close();
stmt.close();
con.close();
8.3 通过JDBC API 访问数据库列子
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
try {
//加载JDBC驱动
Class.forName("com.mysql.jdbc.Driver");
//将JDBC驱动注册到驱动管理器中
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
String url = "jdbc:mysql://localhost:3306/bookdb?useUnicode=true&characterEncoding=GB2312&useSSL=false";
String username = "root";
String password = "admin";
//获取连接
connection = DriverManager.getConnection(url, username, password);
String sql = "SELECT ID,NAME FROM BOOKS WHERE 1=1";
//获取Statement对象
stmt = connection.createStatement();
//执行sql
rs = stmt.executeQuery(sql);
//访问sql中的值
while (rs.next()){
String id = rs.getString("ID");
String name = rs.getString("NAME");
System.out.println(name);
}
}catch (Exception e){
e.printStackTrace();
} finally {
try {
rs.close();
stmt.close();
connection.close();
}catch (SQLException e){
e.printStackTrace();
}
}
8.5 数据源(Data Source)简介
JDBC API 提供了javax.sql.DataSource 接口,它负责建立与数据库的连接,在应用中访问数据库时不必编写连接数据库的代码,可以直接从数据源中获得数据库连接。
1、数据源和数据库连接池
在数据源中事先建立了多个数据库连接,在这些连接保存在数据库连接池中(ConnectionPool)中。java程序访问数据库时,只需要从连接池中取出空闲状态的数据库连接;当程序访问数据库结束,再将连接放回连接池中,这样做可以提高访问数据库的效率。减少了资源的开销。
2、数据源和 JNDI 资源
DataSoruce 对象通常是由 Servlet 容器提供的,因此Java程序无须自己创建DataSource对象,只要直接使用容器提供的DataSource对象即可。
Java 程序获得Servlet容器提供的DataSource对象的引用时依赖 java 的 JNDI(Java Naming and Directory Interface)技术(对象和名字绑定的技术)。
对象工厂负责生产对象,这些对象都和唯一的名字绑定。外部程可以通过名字来获取某个对象的引用。
javax.naming 包中提供了Context 接口,该接口提供了对象和名字绑定,以及通过名字来检索对象的方法。
bind(String name, Object object); //将对象与一个名字绑定
lookup(String name);//返回指定的名字绑定的对象
8.6 配置数据源
为 Web 应用配置数据源涉及修改context.xml 和 web.xml 文件。在 context.xml 文件中加入定义数据源的<Resource>
元素;在web.xml中加入 <resource-ref>
元素,该元素声明 Web 应用引用了特定数据源。
8.6.1 在 context.xml 中加入 <Resource>
元素
<Resource>
元素用来定义 JNDI 资源。在 Tomcat 中,数据源时 JNDI 资源的一种,在helloapp应用的 META-INF 目录下创建一个context.xml文件。在里面定义了一个名为 jdbc/BookDB 的数据源。
<!--在 xml 文件中 & 字符有特殊含义,如果要表达字面上的符号 “&” 需要使用 "&" 转义-->
<Context reloadable="true" >
<Resource name="jdbc/BookDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000"
username="dbuser" password="1234"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/BookDB?
autoReconnect=true&useUnicode=true&characterEncoding=GB2312&useSSL=false"/>
</Context>
<Resource>
元素的属性:
name: 指定Resource 的 JNDI名字。
auth: 指定管理Resource的 Manager,它有两个可选值:Container 和 Application。Container 表示由容器来创建和管理Resource,Application表示由Web应用来创建和管理。
type: 指定Resource 所属的Java类名。
maxActive: 指定数据库连接池中活跃状态的连接的最大数目,为0表示不受限制。
maxIdle: 空闲连接的最大数目,为0 不受限制。
maxWait: 指定数据库连接池中连接处于空闲状态的最时间(毫秒),超过这一时间将会出现异常,为 -1 表示可无限等待。
username: 连接数据库的用户名。
password: 密码。
driverClassName: JDBC 驱动器中Driver 的实现类名。
url: 数据库的 url。
如果希望数据源被 Servlet 容器内一个虚拟主机中的多个 Web 应用访问,那么可以在 server.xml文件中响应的 <Host>
元素中配置<Resource>
子元素。
8.6.2 在 web.xml 中加入 <resource-ref>
元素
如果Web应用访问了由 Servlet 容器管理的某个 JNDI 资源,那么必须在 web.xml 文件中声明对这个 JNDI 资源的引用,表示引用的元素为 <resource-ref>
。
<resource-ref>
<!-- 描述 -->
<description>DB Connection</description>
<!-- 引用资源的名字 与<Resource>中的name属性对应-->
<res-ref-name>jdbc/BookDB </res-ref-name>
<!-- 引用资源的类名字 与<Resource>中的type属性对应-->
<res-type>javax.sql.DataSource</res-type>
<!--指定管理引用资源的 Manager,与<Resource>中的auth属性对应-->
<res-auth>Container</res-auth>
</resource-ref>
8.7 程序中访问数据源
javax.naming.Context提供了查找 JNDI 资源的接口,如对于BookDb的数据源引用:
// 获取Context 对象
Context context = new InitialContext();
//通过lookup()方法的得到数据源
DataSource ds= (DataSource) context.lookup("java:comp/env/BookDb");
//根据数据源获取连接
Connection con = ds.getConnction();
当数据库访问完毕后,调用Connection 的 close() 方法,将连接放回连接池中。
8.7.1 通过数据源连接数据库的 JSP 范例
<!--首先导入一些必要的包-->
<%@ page import="java.io.*"%>
<%@ page import="java.util.*"%>
<%@ page import="java.sql.*"%>
<%@ page import="javax.sql.*"%>
<%@ page import="javax.naming.*"%>
<!--设置中文输出-->
<%@ page contentType="text/html; charset=GB2312" %>
<html>
<head>
<TITLE>dbaccess1.jsp</TITLE>
</head>
<body>
<%
try{
Connection con;
Statement stmt;
ResultSet rs;
//建立数据库连接
Context ctx = new InitialContext();
DataSource ds =(DataSource)ctx.lookup("java:comp/env/jdbc/BookDB");
con = ds.getConnection();
//创建一个SQL声明
stmt = con.createStatement();
//增加新记录
stmt.executeUpdate("insert into BOOKS(ID,NAME,TITLE,PRICE) values ('999','Tom','Tomcat Bible',44.5)");
//查询记录
rs = stmt.executeQuery("select ID,NAME,TITLE,PRICE from BOOKS");
//输出查询结果
out.println("<table border=1 width=400>");
while (rs.next()){
String col1 = rs.getString(1);
String col2 = rs.getString(2);
String col3 = rs.getString(3);
float col4 = rs.getFloat(4);
//打印所显示的数据
out.println("<tr><td>"+col1+"</td><td>"+col2+"</td><td>"+col3+"</td><td>"+col4+"</td></tr>");
}
out.println("</table>");
//删除新增加的记录
stmt.executeUpdate("delete from BOOKS where ID='999'");
//关闭结果集、SQL声明、数据库连接
rs.close();
stmt.close();
con.close();
}catch (Exception e) {
out.println(e.getMessage());e.printStackTrace();
}
%>
</body>
</html>
8.7.2 bookstore 应用通过数据源连接数据库
public class BookDB {
private DataSource ds=null;
public BookDB () throws Exception{
// 初始化数据源
Context ctx = new InitialContext();
if(ctx == null )
throw new Exception("No Context");
ds =(DataSource)ctx.lookup("java:comp/env/jdbc/BookDB");
}
// 从数据源中取出空闲连接
public Connection getConnection()throws Exception{
return ds.getConnection();
}
// 连接使用完后放入连接池
public void closeConnection(Connection con){
try{
if(con!=null) con.close();
}catch(Exception e){
e.printStackTrace();
}
}
// 关闭sql执行对象
public void closePrepStmt(PreparedStatement prepStmt){
try{
if(prepStmt!=null) prepStmt.close();
}catch(Exception e){
e.printStackTrace();
}
}
// 关闭结果集
public void closeResultSet(ResultSet rs){
try{
if(rs!=null) rs.close();
}catch(Exception e){
e.printStackTrace();
}
}
}