目录
JDBC概述
JDBC简介
对于现在的开发几乎所有的项目都是围绕着数据库展开的,很少会见到没有数据库而单独存在的项目,任何的一门语言要想发展,那么必须对数据库有所支持,同样Java从最初的时代就一直支持着数据库的开发标准:JDBC(Java Database Connectivity 、Java数据库链接 ),JDBC本质上讲并不属于一个技术而属于一种服务。所有服务的特征,必须按照指定的套路进行操作。
在Java里面专门为JDBC提供了模块(java.sql),里面的核心开发包也叫java.sqi,在JDBC里面核心的组成就是DriverManager类以及若干接口(Connection、Statement、PreparedStatement、ResultSet)。
对于JDBC的程序数据库访问也分为以下的四种形式:
- JDBC-ODBC桥连接(不会有人再用):利用微软的ODBC技术进行数据库的连接,而后再利用JDBC访问ODBC技术进行程序的开发。程序 -> JDBC -> ODBC -> 数据库,操作性能很差。这个是Java的默认支持技术不需要有额外的配置即可使用;
- JDBC连接:直接利用JDBC进行数据库的连接处理;程序 -> JDBC -> 数据库,这种连接一般只连接本地数据库的服务。
- JDBC网络连接:通过特定网络协议连接指定的数据库服务;程序 -> JDBC ->网络数据库(IP、端口号)。
- JDBC协议连接:自己编写指定的协议操作实现数据库的访问;
Java DataBase Connectivity(Java语言连接数据库)是制定的一套JDBC接口。
eclipse安装JDBC驱动
下载地址:https://dev.mysql.com/downloads/connector/j/,下载后解压:
将bin配置到CLASSPATH之中(使用记事本开发需要配置环境变量)
eclipse配置,在Builder Path中增加:
JDBC编程六步
如果现在要连接MySQL数据库,严格通过以下六步:
-
第一步:注册驱动(告诉Java,使用什么数据库)
-
第二步:获取连接(表示JVM进程和数据库进程之间的通道打开了,属于进程之间的通讯重量级的,使用过后需要关闭通道)
-
第三步:获取数据库操作对象(专门执行sql语句的对象)
-
第四步:执行SQL语句(DQL DML ....)
-
第五步:处理查询结果集(只有第四步执行的是select语句的时候,才有查询结果集)
-
第六步:释放资源(Java和数据库是进程之间的通信,开启之后一定要关闭)
注册驱动和获取连接
URL统一资源定位符,网络中某个资源的绝对路径,URL包括:协议(数据传送格式)、IP、PORT(端口)、资源名,例如:https://www.baidu.com/等同于https://182.61.200.7:80/index.html,其中:
- https:// 通信协议
- 182.61.200.7 服务器IP地址
- 80 服务器上软件的端口
- index.html 服务器上的某个资源名
localhost和127.0.0.1都表示本机IP;
数据注册与获取连接:
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCTest01 {
public static void main(String [] args) {
try {
// 1、注册驱动
Driver driver = new com.mysql.jdbc.Driver(); //多态、父类型引用子类型对象
DriverManager.registerDriver(driver);
// 2、获取连接
String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
String user = "root" ;
String password = "******" ;
Connection conn =DriverManager.getConnection(url, user, password) ;
System.out.println(conn);
} catch (SQLException e) {
e.printStackTrace();
}
// 3、获取数据库操作对象
// 4、 执行SQL语句
// 5、处理查询结果集
// 6、释放资源
}
}
获取数据库操作对象、执行SQL语句、关闭资源
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCTest01 {
public static void main(String [] args) {
Connection conn = null;
Statement stmt = null;
try {
// 1、注册驱动
Driver driver = new com.mysql.jdbc.Driver(); //多态、父类型引用子类型对象
DriverManager.registerDriver(driver);
// 2、获取连接
String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";
String user = "root" ;
String password = "******" ;
conn =DriverManager.getConnection(url, user, password) ;
System.out.println(conn);
// 3、获取数据库操作对象
stmt = conn.createStatement() ;
// 4、 执行SQL语句
String sql = "insert into dept(deptno,dname,loc) values(50, '人事部', '北京');" ;
// 专门执行DML语句的(insert delete update),返回值是影响数据库中的记录条数
int count = stmt.executeUpdate(sql);
System.out.println(count == 1 ? "保存成功" : "保存失败");
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 6、释放资源
// 从小到大依次关闭,并且对其try ... catch
try {
if (stmt != null) { // 不能一起try,如果一个try,stmt的出现异常,就不执行conn的
stmt.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
JDBC深入分析与代码重构
连接MySQL数据库
第一:通过反射机制加载数据库程序驱动类:com.mysql.jdbc.Driver;
第二:获取连接,对于数据库的连接提供有java.sql.DriverManager的程序类,利用此类中的方法可以获取一个Connection的接口对象:
- 获取Connection接口对象:public static Connection getConnection(String url, String user, String password) throws SQLException
范例:实现数据库连接操作
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "***********" ;
public static void main(String [] args) throws Exception {
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
System.out.println(conn);
conn.close();
}
}
如果此时输出Connection接口对象的时候,并没有产生任何的异常则表示当前的mysql数据库连接成功了。
为什么连接MySQL数据库的时候一定要单独配置MySQL驱动程序?JDBC是一个标准里面没有具体的实现类,下面将通过类图的关系来进行描述。
通过结构可以发现,整个的JDBC设计实现的就是一个工厂类的处理机制。DriverManager是一个工厂,不同数据库的生产商利用JDBC提供的标准(接口)实现各自的数据库处理操作。
Statement接口简介
当获取了java.sql.Connection接口对象之后,那么其核心目的不是为了连接而是为了数据库的操作,而进行数据库的开发操作应该使用标准的SQL语句开发完成,所以需要有一个SQL的执行器,而这个执行器就可以利用Statement接口完成。
java.sql.Statement是JDBC之中提供的数据库操作接口,那么利用其可以实现数据的更新与查询的处理操作。
-
public interface Statement extends Wrapper, AutoCloseable
该接口是AutoCloseable子接口,所以可以得出结论:每一次进行数据库操作完成之后都应该关闭Statement操作,即一条SQL的执行一定是一个Statement的对象,但是如果要想获取Statement接口对象,那么必须依靠Connection接口提供的方法完成:
- 获取Statement接口对象:Statement createStatement() throws SQLException
|- 此时抛出的SQLEXception是JDBC数据库开发之中的最大异常;
这个里面含门面设计模式,按照固定标准来进行,做完第一步才能左第二步....,比如:你早上要去约会,要起床穿衣服再去,不可能边走边穿。 这里先获取Connection接口对象之后,才创建Statement。
当获取了Statement接口对象之后,就可以使用SQL对象进行处理了,而这里面需要两个方法的支持:
- 数据更新处理(insert、update、delete):public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException;
- 数据查询处理(select、统计查询、复杂查询):public ResultSet executeQuery(String sql) throws SQLException
这两个数据库的操作方法里面都需要接收SQL的字符串,也就是说Statement接口可以直接使用SQL语句实现开发。
范例:定义数据库脚本
drop table if exists news;
create table news(
nid int primary key auto_increment,
title varchar(30),
readw int,
price float,
content varchar(255),
pubdate Date
);
数据更新操作
在SQL语句之中数据的更新操作一共分为三种:i增加(insert)、修改(update)、删除(delete)。Statement接口的最大特点是可以直接执行一个标准的SQL语句。
对于SQL语句而言,由于一般比较长,所以需要考虑换行编写,一旦换行,建议在每个字符串前后多追加一个空格,避免错误带来的麻烦。
范例:实现数据的增加处理
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "******" ;
public static void main(String [] args) throws Exception {
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
System.out.println(conn);
// 3、创建数据库的操作对象
Statement stmt = conn.createStatement();
String sql = " insert into news(title,readw,price,content,pubdate) "
+ " values ('REN_news', 10, 9.9, 'learning needs a good teacher or book', "
+ " '2020-5-20') ";
int count = stmt.executeUpdate(sql) ; // 返回影响的行数
System.out.println("更新操作影响的数据行数" + count);
conn.close(); // 注意连接一关所有的都关了,所以不用单独去关闭stmt
}
}
范例:数据的更新处理,一般是条件性的更新,不能全部更新
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "******" ;
public static void main(String [] args) throws Exception {
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
System.out.println(conn);
// 3、创建数据库的操作对象
Statement stmt = conn.createStatement();
String sql = "update news set content = 'it is waring',readw=9999 where nid = 1 ";
int count = stmt.executeUpdate(sql) ; // 返回影响的行数
System.out.println("更新操作影响的数据行数" + count);
conn.close(); // 注意连接一关所有的都关了,所以不用单独去关闭stmt
}
}
范例:数据删除
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "******" ;
public static void main(String [] args) throws Exception {
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
System.out.println(conn);
// 3、创建数据库的操作对象
Statement stmt = conn.createStatement();
String sql = " delete from news where nid in (1,3,5,7,9,11,17);";
int count = stmt.executeUpdate(sql) ; // 返回影响的行数
System.out.println("更新操作影响的数据行数" + count);
conn.close(); // 注意连接一关所有的都关了,所以不用单独去关闭stmt
}
}
数据修改只是考虑不同的SQL语句即可,这也是Statement接口的最大特点(直接执行SQL语句);
数据查询
数据更新主要是接收其影响的数据行数,但是数据查询就需要将结果返回给程序,由程序进行结果的处理,所以在Java里面通过ResultSet接口描述查询的结果。ResultSet操作流程如下:
范例:实现数据的查询处理
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "******" ;
public static void main(String [] args) throws Exception {
// 在程序开发中select后面必须跟上具体的字段名称,写“*”都叫垃圾代码
String sql = " select nid,title,readw,price,content,pubdate from news" ;
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
System.out.println(conn);
// 3、创建数据库的操作对象
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql) ; // 执行查询,返回ResultSet
while(rs.next()) { // 发现还有数据行未输出
int nid = rs.getInt(1);
String title = rs.getString(2);
int readw = rs.getInt(3) ;
double price = rs.getDouble(4) ;
String content = rs.getString(5) ;
Date pubdate = rs.getDate(6) ;
System.out.println(nid + "、" + title + "、" + readw +
"、" + price + "、" + content + "、" + pubdate);
}
conn.close(); // 注意连接一关所有的都关了,所以不用单独去关闭stmt
}
}
需要注意的是,ResultSet对象是保存在内存之中的,如果说你查询数据的返回结果过大,那么程序也会出现问题。
PreparedStatement数据库接口
既然java.sql包里面提供有Statement接口可以实现数据库的操作,那么为什么又要提供有一个PreparedStatement接口实现数据库的操作?
Statement接口问题
下面以更新的操作为例,在Statement接口里面如果要想执行SQL语句,那么一定要通过字符串实现SQL结构的定义,但是这种定义如果要结合到用户输入数据的情况下就可能会有问题存在。利用Statement执行的SQL语句有如下三种问题:
- 不能很好的描述出日期形式;
- 需要进行SQL语句的拼凑处理,而导致SQL语句的编写与维护困难;
- 对于一些敏感的字符数据无法进行合理拼凑;
所以现在可以发现,虽然Statement可以操作数据库,但是 在其操作过程中并不是那么的方便,它最大的问题是需要进行SQL语句的拼凑。
PreparedStatement操作数据库
为了解决Statement接口存在的SQL执行的问题,所以在java.sql包里面提供有一个Statement的子接口:PreparedStatement,这个接口最大的好处是可以编写正常的SQL(数据不再和SQL的语法混合在一起),同时利用占位符的形式可以在SQL正常执行完毕后可以进行数据的设置。观察PreparedStatement的接口定义形式:
public interface PreparedStatement extends Statement
如果要想获取PreparedStatement接口的实例,依然需要通过Connection接口实现,创建方法:
- 创建PreparedStatement接口对象:PreparedStatement prepareStatement(String sql) throws SQLException
由于SQL语句已经在创建PreparedStatement接口对象的时候提供了,所以在执行数据库操作的时候也要更换方法:
- 数据库更新:public int executeUpdate() throws SQLException
- 数据库查询:public ResultSet executeQuery() throws SQLException
在JDBC中无论是PreparedStateMnet还是使用ResultSet获取的日期时间都是jaia.util.Date的子类,关系如下:
需要把util的Date转为sql的date
范例:利用PreparedStatement实现数据库的操作
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Date;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "******" ;
public static void main(String [] args) throws Exception {
String title = "REN新闻";
int readw = 99;
double price = 9.9 ;
String content = "想休息了" ;
Date pubdate = new Date();
String sql = " insert into news(title,readw,price,content,pubdate) "
+ " values (?, ?, ?, ?, ?) "; // 使用?作为占位符
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
System.out.println(conn);
// 3、创建数据库的操作对象
PreparedStatement pstmt = conn.prepareStatement(sql);
// 在执行具体的数据库草走之前需要为占位符设置内容,按照顺序设置
pstmt.setString(1, title);
pstmt.setInt(2, readw);
pstmt.setDouble(3, price);
pstmt.setString(4, content);
pstmt.setDate(5, new java.sql.Date(pubdate.getTime()));
int count = pstmt.executeUpdate() ; // 返回影响的行数
System.out.println("更新操作影响的数据行数" + count);
conn.close(); // 注意连接一关所有的都关了,所以不用单独去关闭stmt
}
}
PreparedStatement实现数据查询操作
清楚了PreparedStatement实现更新处理之后,那么下面来实现数据查询处理操作,由于在开发之中PreparedStatement的使用是最为广泛的,下面将列举几个有代表性的查询:
1、查询全部数据
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "******" ;
public static void main(String [] args) throws Exception {
// 在程序开发中select后面必须跟上具体的字段名称,写“*”都叫垃圾代码
String sql = " select nid,title,readw,price,content,pubdate from news" ;
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
// 3、创建数据库的操作对象
PreparedStatement pstmt = conn.prepareStatement(sql);
//
ResultSet rs = pstmt.executeQuery() ;
while(rs.next()) { // 发现还有数据行未输出
int nid = rs.getInt(1);
String title = rs.getString(2);
int readw = rs.getInt(3) ;
double price = rs.getDouble(4) ;
String content = rs.getString(5) ;
Date pubdate = rs.getDate(6) ;
System.out.println(nid + "、" + title + "、" + readw +
"、" + price + "、" + content + "、" + pubdate);
}
conn.close(); // 注意连接一关所有的都关了,所以不用单独去关闭stmt
}
}
2、根据id进行数据查询
package cn.ren.jdbc;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JDBCTest01 {
private static final String DATABASE_DRIVER = "com.mysql.jdbc.Driver"; // 驱动程序
private static final String DATABASE_URL = "jdbc:mysql://127.0.0.1:3306/bjpowernode" ;
private static final String DATABASE_USER = "root";
private static final String DATABASE_PASSWORD = "******" ;
public static void main(String [] args) throws Exception {
// 在程序开发中select后面必须跟上具体的字段名称,写“*”都叫垃圾代码
String sql = " select nid,title,readw,price,content,pubdate from news where nid=?" ;
// 1、向容器之中加载数据库驱动程序
Class.forName(DATABASE_DRIVER) ; // 向容器之中加载数据库驱动程序
// 2、获取连接
Connection conn = null; // 每一个Connection的对象描述的都是一个用户的连接
conn = DriverManager.getConnection(DATABASE_URL, DATABASE_USER, DATABASE_PASSWORD) ;
// 3、创建数据库的操作对象
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 18); // 设置nid
//在执行数据之前需要为占位符设置内容
ResultSet rs = pstmt.executeQuery() ;
while(rs.next()) { // 发现还有数据行未输出
int nid = rs.getInt(1);
String title = rs.getString(2);
int readw = rs.getInt(3) ;
double price = rs.getDouble(4) ;
String content = rs.getString(5) ;
Date pubdate = rs.getDate(6) ;
System.out.println(nid + "、" + title + "、" + readw +
"、" + price + "、" + content + "、" + pubdate);
}
conn.close(); // 注意连接一关所有的都关了,所以不用单独去关闭stmt
}
}
3、在进行全部数据查询的时候如果返回内容过多则一定会造成内存的大量占用,那么此时可以使用分页的形式实现数据的查询处理。
4、统计指定关键字的新闻数量