Javaweb学习笔记(JDBC入门)
JDBC概述
JDBC全称是Java数据库连接,是一套用于执行SQL语句的Java API,应用程序可以通过JDBC连接到关系数据库,并使用SQL语句来完成对数据库中数据的查询、更新和删除操作。
JDBC实现包括三部分:
(1)JDBC驱动管理器:负责注册特定的JDBC驱动器,主要通过java.sql.DriverManager类实现。
(2)JDBC驱动器API:最主要的接口是java.sqlDriver接口
(3)JDBC驱动器:一种数据库驱动,也称JDBC驱动程序,实现了JDBC驱动器API负责与特定的数据库连接,以及处理通信细节。
JDBC常用API
(1)Driver接口
Driver接口是所有JDBC驱动程序都必须实现的接口,该接口专门提供给数据库厂商使用,在编写JDBC程序时必须要把制定数据库驱动程序或类库加载到项目classpath中。
(2)DriverManager类
DriverManager类用于加载JDBC驱动并且创建于数据库的连接,其中定义了两个比较重要的静态方法:
方法名称 | 功能描述 |
---|---|
registerDriver(Driver Driver) | 该方法用于向DriverManager中注册的指定的JDBC驱动程序 |
getConnection(String url,String user,String pwd) | 该方法用于建立和数据库的连接,并返回表示连接的Connection对象。 |
(3)Connection接口
Connection接口代表Java程序和数据库连接,在Connection接口中,定义了一系列方法:
方法名称 | 功能描述 |
---|---|
getMetaData() | 该方法用于返回表示数据库的元数据的DatabaseMetaData对象 |
createStatement() | 用于创建一个Statement对象来将SQL语句发送到数据库 |
prepareStatement(String sql) | 用于创建一个prepareStatement对象将参数化的SQL语句发送到数据库 |
prepareCall(String sql) | 用于创建一个CallableStatement对象来调用数据库存储过程 |
(4)Statement接口
Statement接口用于向数据库发送SQL语句,在Statement接口中,提供了三个执行SQL语句的方法。
方法名称 | 功能描述 |
---|---|
excute(String sql) | 用于执行各种SQL语句,该方法返回一个boolean类型的值,如果为true表示执行的SQL语句具备查询结果,可以通过Statement的getResultSet()方法获得查询结果 |
excuteUpdate(String sql) | 用于执行SQL中的insert、update和delete语句,该方法返回一个int类型的值,表示数据库受该SQL语句影响的记录的数目。 |
excuteQuery(String sql) | 用于执行SQL语句中的select语句,该方法返回一个表示查询结果的ResultSet对象 |
(5)PreparedStatement接口
PrepareStatement是Statement的子接口,用于执行预编译的SQL语句。
方法名称 | 功能描述 |
---|---|
excuteUpdate() | 在此PrepareStatement对象中执行SQL语句,该语句必须是一个DML语句或者是无返回内容的SQL语句 |
excuteQuery() | 在此PrepareStatement对象中执行SQL语句,该方法返回的是ResultSet对象 |
setInt(int paremeterIndex,int x) | 将指定参数设置为给定的int值 |
setFloat(int pareameterIndex,float x) | 将指定参数设置为给定的float值 |
setString(int parameterIndex,String x) | 将指定参数设置为给定的String值 |
setDate(int parameterIndex,Dtae x) | 将指定参数设置为给定的Date值 |
addBatch() | 将一组参数添加到此PreparedStatement对象的批处理命令 |
setCharacterStream(parameterIndex reader,length) | 将指定的输入流写入数据库的文本字段 |
setBinaryStream(parameterIndex,x,length) | 将二进制的输入流数据写入到二进制字段中 |
(6)CallableStatement接口
CallableStatement是PreparedStatement的子接口,用于执行SQL存储过程。
方法名称 | 功能描述 |
---|---|
registerOutParamenter(int pareameterIndex,int sqlType) | 按顺序位置将OUT参数注册为SQL类型。其中paremeterIndex表示顺序位置,sqlType表示SQL类型 |
setNull(String paramterName,int sqlType) | 将指定参数设置为SQL类型的NULL |
setString(String parameterName,String x) | 将指定参数设置为给定的Java类型的String |
wasNull() | 查询最后一个读取的OUT参数是否为SQL类型的NULL值 |
getInt(int parameterIndex) | 以Java语言中int值的形式获取指定的数据库中的INTEGER类型的值 |
(7)ResultSet接口
ResuleSet接口表示select查询语句得到的结构集,该结果集封装在一个逻辑表格中。在ResultSet接口内部有一个指向表格数据行的游标,ResultSet对象初始化时,游标在表格的第一行之前。
方法名称 | 功能描述 |
---|---|
getString(int columnIndex) | 用于获取指定字段String类型的值,参数columnIndex代表字段的索引。 |
getString(String columnName) | 用于获取指定字段String类型的值,参数columnIndex代表字段的名称 |
getInt(int columnIndex) | 用于获取指定字段int类型的值,参数columnIndex代表字段的索引 |
getInt(String columnIndex) | 用于获取指定字段类型的值 |
getDate(int columnIndex) | 用于获取指定字段的int类型的值,参数columnIndex代表字段的索引 |
getDate(String columnIndex) | 用于获取指定字段的int类型的值,参数columnIndex代表字段的名称 |
next() | 将游标从当前位置向下移一行 |
absolute(int row) | 将游标移动到此ResultSet对象的指定行 |
afterLast() | 将游标移动到此ResultSet对象的末尾,级最后一行之后 |
beforeFirst() | 将游标移动到ResultSet对象的开头 |
previous() | 将游标移动到此ResultSet对象的上一行 |
last() | 将游标移动到此ResultSet对象的最后一行 |
实现第一个JDBC程序
JDBC编程步骤为:
(1)加载并注册数据库驱动
DriverManager.registerDriver(Driver driver);
(2)通过DriverManager获取数据库连接,具体方式如下:
Connection coon=DriverManager.getConnection(String url.String user,String pass);
getConnection有三个参数,分别是数据库url和登录数据库的用户名和密码
数据库url遵循如下形式的写法:
jdbc:subrotocol:subname
上面url写法中的jdbc的写法是固定的,subrotocol指定连接到特定数据库的驱动程序,而subname部分很不固定。
(3)通过Connection对象获取Statement对象,Connection创建Statement的方式有如下三种:
1.createStatement():创建基本的Statement
2.prepareStatement():创建PrepareStatement对象
3.prepareCall():创建CallableStatement对象
例如:
Statement stmt=conn.createStatement();
(4)使用Statement执行Sql语句是查询语句,所有的Statement都有如下三种方法来执行SQL语句。
1.excute():可以执行任何SQL语句
2.excuteQuery():通常执行查询语句,执行并返回代表结果集的ResultSet对象。
3.excuteUpdate():主要用于执行DML和DDL语句,执行DML语句。
(5)操作ResultSet结果集,如果执行的SQL语句是查询语句,执行结果将返回一个ResultSet对象,该对象里保存了SQL语句的查询结果。ResultSet对象提供的方法主要分为两类:
1.next()、previous()、first()、last()、beforeFirst()、afterLast()、absolute()等移动记录指针的方法
2.getXxx()获取指针指向行,特定列的值。
(6)回收数据库资源,关闭数据库连接,释放资源,包括关闭ResultSet、Statement和Connection等资源。
1.搭建实验环境
在MySQL中创建一个名称为chapter01的数据库,然后在数据库中创建一个users表语句如下
create database chapter01;
ERROR 1007 (HY000): Can't create database 'chapter01'; database exists
mysql> use chapter01;
Database changed
mysql> create table users(
id int primary key auto_increMent,
name varchar(40),
password varchar(40),
email varchar(60),
birthday date
)character set utf8 collate utf8_general_ci;
数据库和表创建成功后,再向user表中插入三条数据,SQL语句如下:
insert into users(name,password,email,birthday)
value('zs','123456','zs@sina.com','1980-12-04');
insert into users(name,password,email,birthday)
value('lisi','123456','lisi@sina.com','1981-12-04');
insert into users(name,password,email,birthday)
value('wangwu','123456','wangwu@sina.com','1979-12-04');
使用select语句查询users表
导入数据库驱动
新建java工程chapter01,将要访问的数据库驱动文件添加到classpath中。
编写JDBC程序
在工程chapter01中新建Java类Example01
package cn.itcast.jdbc.example;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Date;
import java.sql.Connection;
import java.sql.ResultSet;
public class Example01 {
public static void main(String[] args)throws SQLException{
//1.注册数据库的驱动
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.通过数据库DriverManager获取数据库连接
String url="jdbc:mysql://localhost:3306/chapter01";
String username="root";
String password="123456";
Connection conn=DriverManager.getConnection(url, username, password);
//3.通过Connection对象获取Statement对象
Statement stmt=conn.createStatement();
//4.使用Statement执行SQL语句
String sql="select * from users";
ResultSet rs=stmt.executeQuery(sql);
//5.操作Result结果集
System.out.println("id|name|password|email|birthday");
while(rs.next()){
int id=rs.getInt("id");
String name=rs.getString("name");
String psw=rs.getString("password");
String email=rs.getString("email");
String birthday=rs.getString("birthday");
System.out.println(id+"|"+name+"|"+psw+email+"|"+birthday);
}
rs.close();
stmt.close();
conn.close();
}
}
注意:
程序仍然有两个方面需要进行改进
1.注册驱动
在注册驱动时,虽然DriverManager.registerDriver(new com.mysql.jdbc.Driver())方法可以完成,但会使数据库驱动被注册两次。这是因为Driver类的源代码中已经在静态代码中完成了注册。需要加载驱动类:
Class.forName("com.mysql.jdbc.Driver")
2.释放资源
由于数据库资源十分宝贵,数据库允许的并发访问的连接数量有限,因此,当数据库资源使用完毕后,一定要释放资源,资源释放的代码如下:
if(rs!=null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
rs=null;
}
if(stmt!=null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
stmt=null;
}
if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn=null;
}
PrepareStatement对象
Statement对象没词执行SQL语句时都会对其进行编译,降低了数据库的访问效率。PrepareStatement是Statement的一个子类,可以对SQL语句进行预编译,当SQL语句再次执行时,数据库只需使用缓冲区中的数据,而不需要对SQL语句进行编译。
示例:Example02
public class Example02 {
public static void main(String[] args)throws SQLException{
Connection conn=null;
PreparedStatement preStmt=null;
try{
//加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/chapter01";
String username="root";
String password="123456";
conn=DriverManager.getConnection(url,username,password);
String sql="INSERT INTO users(name,password,email,birthday)"+"VALUES(?,?,?,?)";
preStmt=conn.prepareStatement(sql);
preStmt.setString(1, "zl");
preStmt.setString(2, "123456");
preStmt.setString(3, "zl@sina.com");
preStmt.setString(4, "1789-12-23");
preStmt.executeUpdate();
}catch(ClassNotFoundException e){
e.printStackTrace();
}finally{
if(preStmt!=null){
try{
preStmt.close();
}catch(SQLException e){
e.printStackTrace();
}
preStmt=null;
}
}
if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn=null;
}
}
}
CallableStatement对象
CallableStatement接口是用于执行SQL储存过程的接口,继承来自PrepareStatement接口。JDBC API提供了一个存储过程SQL转义语法,该语法允许对所有关系型数据库管理系统使用标准方式调用存储过程。此语法有一个包含结构参数的形式和一个不包含结果参数的形式,具体如下:
{?=call<procedure-name>[(<arg1>,<arg2>...)]}
{call<procedure-name>[(<arg1>,<arg2>...)]}
上述语法中的< arg1>< arg2>有三种不同的形式,具体如下:
(1)IN类型:此类型是用于参数从外部传递给存储过程使用
(2)OUT类型:此类型是存储过程执行过程中的返回值
(3)IN、OUT混合型:此类型是参数传入,然后返回
如果使用结果参数,则必须将其注册为OUT参数,参数是根据编号按顺序引入的买第一个参数的编号是1。
调用存储过程使用CallableStatement,可以通过Connection的prepareCall()方法来创建CallableStatement对象,创建该对象时需要传入迪奥哟个存储过程的SQL语句,示例如下:
CallableStatement cstmt=conn.prepareCall("{Call add_pro(?,?,?)}");
其中?可以是IN,OUT或者INOUT参数,这取决于add_pro。
传入参数是Java程序必须为这些参数传入值,那么通过CallableStatement对象的setXxx()方法为传入的参数设置值;所谓传出参数,就是Java程序可以通过该参数获取存储过程里的值。
示例:
cstmt.registerOutParameter(3,Types.INTEGER);
示例:
Example03
首先在MySQL中创建名为add_pro的存储过生,该过程包含三个参数:a,b,c。
代码如下:
delimiter //
mysql> create procedure add_pro(a int,b int,out sum int)
begin
set sum=a+b;
end//
delimiter;
然后创建一个Ex03
代码如下:
package cn.itcast.jdbc.example;
import java.sql.*;
public class Ex03 {
public static void main(String[] args)throws Exception{
Connection conn=null;
CallableStatement cstmt=null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/chapter01";
String username="root";
String password="123456";
conn=DriverManager.getConnection(url, username, password);
cstmt=conn.prepareCall("call add_pro(?,?,?)");
cstmt.setInt(1, 4);
cstmt.setInt(2, 5);
cstmt.registerOutParameter(3, Types.INTEGER);
cstmt.execute();
System.out.println("执行结果是:"+cstmt.getInt(3));
}finally{
if(cstmt!=null){
cstmt.close();
}
if(conn!=null){
conn.close();
}
}
}
}
ResultSet对象
ResultSet主要用于存储结果集,并且只能通过next()方法由前向后逐个获取结果集中的数据。但是如果想获取结果集中任意位置的数据,则需要在创建Statement对象,设置两个ResultSet定义的常量,具体设置方式如下:
Statement st=conn.createStatement(ResultSet.TYPE_SCROLL_INSENITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet rs=st.excuteQuery(sql);
在上述方式中,常量ResultSet.TYPE_SCROLL_INSENITIVE表示结果集可以滚动,ResultSet.CONCUR_READ_ONLY表示以只读形式打开结果集。
示例:
package cn.itcast.jdbc.example;
import java.sql.*;
public class Example04 {
public static void main(String[] args)throws Exception{
Connection conn=null;
CallableStatement stmt=null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/chapter01";
String username="root";
String password="123456";
conn=DriverManager.getConnection(url, username, password);
String sql="select * from users";
Statement st=conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet rs=st.executeQuery(sql);
System.out.print("第二行数据的name值为:");
rs.absolute(2);
System.out.println(rs.getString("name"));
System.out.print("第三行数据的name值为:");
rs.beforeFirst();//将指针定位到结果集中第1行数据之前
rs.next();//将指针向后滚动
System.out.println(rs.getString("name"));
System.out.print("第四行数据的name值为:");
rs.afterLast();//将指针定位到结果集中最后一条数据之后
rs.previous();//将指针向前滚动
System.out.println(rs.getString("name"));
}catch(Exception e){
e.printStackTrace();
}finally{
if(stmt!=null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
stmt=null;
}
if(conn!=null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
conn=null;
}
}
}
}
案例——JDBC的基本操作
(1)在工程chapter01下创建一个包cn.itcast.jdbc.example.domin,并在该包中创建保存一个用户名的User类
package cn.itcast.jdbc.example.domin;
import java.util.Date;
public class User {
private int id;
private String username;
private String password;
private String email;
private Date birthday;
public int getId(){
return id;
}
public void setId(int id){
this.id=id;
}
public String getUsername(){
return username;
}
public void setUsername(){
this.username=username;
}
public String getPassword(){
return password;
}
public void setPassword(){
this.password=password;
}
public String getEmail(){
return email;
}
public void setEmail(){
this.email=email;
}
public Date getBirthday(){
return birthday;
}
public void setBirthday(){
this.birthday=birthday;
}
}
(2)没词加载数据库驱动时,都需要加载数据库驱动、建立数据库连接以及关闭数据库连接,为避免代码重复,新建一个包cn.itcast.jdbc.example.utils,并在包中建立一个工具类JDBCUtils,JDBCUtils的具体实现方式:
package cn.itcast.jdbc.example.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCUtils {
//加载驱动,并建立数据库连接
public static Connection getConnection()throws SQLException,ClassNotFoundException{
Class.forName("com.mysql.cj.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/chapter01";
String username="root";
String password="123456";
Connection conn=DriverManager.getConnection(url, username, password);
return conn;
}
//关闭数据库连接释放资源
public static void release(Statement stmt,Connection conn){
if(stmt!=null){
try{
stmt.close();
}catch (SQLException e){
e.printStackTrace();
}
stmt=null;
}
if(conn!=null){
try{
conn.close();
}catch (SQLException e){
e.printStackTrace();
}
conn=null;
}
}
public static void release(ResultSet rs,Statement stmt,Connection conn){
if(rs !=null){
try{
rs.close();
}catch (SQLException e){
e.printStackTrace();
}
rs=null;
}
release(stmt,conn);
}
}
(3)新建一个包cn.itcast.jdbc.example.dao,在包中创建一个类UserDao,该类封装了对表users的添加、查询、删除和更新等操作
package cn.itcast.jdbc.example.dao;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import cn.itcast.jdbc.example.utils.*;
import cn.itcast.jdbc.example.domin.User;
public class UserDao {
public boolean insert(User user){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try{
//获取数据的连接
conn=JDBCUtils.getConnection();
stmt=conn.createStatement();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String birthday=sdf.format(user.getBirthday());
String sql="INSERT INTO users(id,name,password,email,birthday)"
+ "VALUES("+user.getId()+",'"+user.getUsername()+"','"+user.getPassword()+"','"+user.getEmail()+"','"+birthday+"')";
int num=stmt.executeUpdate(sql);
if(num>0){
return true;
}
return false;
}catch (Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs,stmt,conn);
}return false;
}
//查询所有的User对象
public ArrayList<User>findAll(){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
ArrayList<User>list=new ArrayList<User>();
try{
//获得数据的连接
conn=JDBCUtils.getConnection();
//获得Statement对象
stmt=conn.createStatement();
//发送SQL语句
String sql="SELECT *FROM users";
rs=stmt.executeQuery(sql);
while (rs.next()){
User user=new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setEmail(rs.getString("Email"));
user.setBirthday(rs.getDate("birthday"));
list.add(user);
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}
return null;
}
//根据id查找指定user
public User find(int id){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try{
//获得数据的连接
conn=JDBCUtils.getConnection();
//获得Statement对象
stmt=conn.createStatement();
//发送SQL语句
String sql="SELECT * FROM users WHERE id="+id;
rs=stmt.executeQuery(sql);
while(rs.next()){
User user=new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("name"));
user.setPassword(rs.getString("password"));
user.setEmail(rs.getString("Email"));
user.setBirthday(rs.getDate("birthday"));
return user;
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}
return null;
}
//删除用户
public boolean delete(int id){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try{
//获得数据的连接
conn=JDBCUtils.getConnection();
//获得Statement对象
stmt=conn.createStatement();
//发送SQL语句
String sql="DELETE FROM users WHERE id="+id;
int num=stmt.executeUpdate(sql);
if(num>0){
return true;
}
return false;
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}
return false;
}
//修改用户
public boolean update(User user){
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try{
//获得数据的连接
conn=JDBCUtils.getConnection();
//获得Statement对象
stmt=conn.createStatement();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String birthday=sdf.format(user.getBirthday());
String sql="UPDATE users set name='"+user.getUsername()+"',password='"+user.getPassword()+"',email='"+user.getEmail()+"',birthday='"+birthday+"'WHERE id="+user.getId();
int num=stmt.executeUpdate(sql);
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtils.release(rs, stmt, conn);
}
return false;
}
}
(4)编写测试类Example05,实现向User表中添加数据
示例:
package cn.itcast.jdbc.example;
import java.util.*;
import cn.itcast.jdbc.example.domin.*;
import cn.itcast.jdbc.example.dao.*;
public class Example05 {
public static void main(String[] args){
UserDao ud=new UserDao();
User user=new User();
user.setId(5);
user.setUsername("h1");
user.setPassword("123");
user.setEmail("h1@sina.com");
user.setBirthday(new Date());
boolean b=ud.insert(user);
System.out.println(b);
}
}