JDBC小记
前言
昨天刚冲完了jdbc,今天来总结
一.JDBC介绍
JDBC (百度百科): Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC也是Sun Microsystems的商标。我们通常说的JDBC是面向关系型数据库的。
说白了 JDBC就是Java数据库连接,就是用Java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句.
二.JDBC原理图
三.JDBC的本质:
JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
JDBC到底是什么
SUN公司制定的一套接口(interface),(在java.sql.*;包下有很多接口)
目的:解耦合——>降低程序的耦合度,提高程序的扩展力
**附:**java链接MySQL驱动包最新版下载地址:https://dev.mysql.com/downloads/connector/j/,解压后得到jar库文件,然后在项目中导入该库文件.
博主使用的版本是mysql-connector-java-5.1.47.jar
四.什么是驱动
驱动,是指驱动计算机里软件的程序。驱动程序全称设备驱动程序,是添加到操作系统中的特殊程序,其中包含有关硬件设备的信息。此信息能够使计算机与相应的设备进行通信。驱动程序是硬件厂商根据操作系统编写的配置文件,可以说没有驱动程序,计算机中的硬件就无法工作。
例如:网卡,声卡,显卡等等 必须要安装驱动程序,不然这些硬件就无法正常工作。
五.为什么要面向接口编程?
解耦合:降低程序的耦合度,提高程序的扩展力
多态机制就是非常典型的:面向抽象编程(不要面向具体编程)
Animal a = new Cat();
Animal a = new Dog();
//喂养的方法
public void feed(Animal a ){ //面向父类型编程}
不建议:
Dog d = new Dog();
Cat c = new Cat();
思考:为什么sun公司要制定一套JDBC接口呢?
因为每个数据库的实现原理不一样,oracle、mysql、ms sqlserver都有自己的原理,每个数据库产品都有自己独特的实现原理.
六.JDBC核心类(接口)介绍
JDBC中的核心类有:DriverManager
、Connection
、Statement
,和ResultSet
DriverManger(驱动管理器)的作用有两个:
-
注册驱动:这可以让JDBC知道要使用的是哪个驱动;
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
-
获取Connection:如果可以获取到Connection,那么说明已经与数据库连接上了。
DriverManager.getConnection(url,username,password)
Connection对象表示连接,与数据库的通讯都是通过这个对象展开的:
-
Connection最为重要的一个方法就是用来获取Statement对象;
Statement stmt = con.createStatement();
-
Statement是用来向数据库发送SQL语句的,这样数据库就会执行发送过来的SQL语句
-
void executeUpdate(String sql):执行更新操作(insert、update、delete等);
-
ResultSet executeQuery(String sql):执行查询操作,数据库在执行查询后会把查询结果,查询结果就是ResultSet;
ResultSet对象表示查询结果集,只有在执行查询操作后才会有结果集的产生。结果集是一个二维的表格,有行有列。操作结果集要学习移动ResultSet内部的“行光标”,以及获取当前行上的每一列上的数据:
-
boolean next():使“行光标”移动到下一行,并返回移动后的行是否存在;
rs.next();//光标移动到第一行 rs.getInt(1);//获取第一行第一列的数据
-
XXX getXXX(int col):获取当前行指定列上的值,参数就是列数,列数从1开始,而不是0。
//常用的方法 Object getObject(int col) String getString(int col) int getInt(int col) double getDouble(int col)
七.JDBC编程6步(超级重要,需要背会!!!)
第1步:注册驱动 (只做一次)
第2步:获取数据库连接对象(Connection)
第3步:获取数据库操作对象(Statement)
第4步:执行sql语句(增删改查)
第5步:处理查询结果集(ResultSet)
第6步:释放资源
第1步:注册驱动 (只做一次)
作用:告诉java程序,即将要连接的是哪个牌子的数据库.
注册驱动的两种方法 (例如,注册MySQL的数据库驱动) :
// 第一种: 推荐
Class.forName(“com.mysql.jdbc.Driver”);
Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。
// 第二种: 不推荐
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// DriverManager类的registerDriver()方法的参数是java.sql.Driver,但java.sql.Driver是一个接口,实现类由mysql驱动来提供,mysql驱动中的java.sql.Driver接口的实现类为com.mysql.jdbc.Driver
上面代码虽然可以注册驱动,但是出现硬编码(代码依赖mysql驱动jar包),如果将来想连接Oracle数据库,那么必须要修改代码的。并且其实这种注册驱动的方式是注册了两次驱动!
通过初始化驱动类com.mysql.jdbc.Driver
,该类就在 mysql-connector-java-5.0.8-bin.jar中。如果你使用的是oracle数据库那么该驱动类将不同。
**注意:**Class.forName需要捕获ClassNotFoundException.
try {Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace();}
第2步:获取数据库连接对象(Connection)
表示jvm的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完一定要关闭
可以使用 DriverManager.getConnection()
方法建立连接。根据传入参数的不同,有三种重载的DriverManager.getConnection()
方法:
getConnection(String url)
getConnection(String url, Properties prop)
getConnection(String url, String user, String password)
还可以在url中提供参数:
jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF8
useUnicode参数指定这个连接数据库的过程中,使用的字节集是Unicode字节集;
characherEncoding参数指定穿上连接数据库的过程中,使用的字节集编码为UTF-8编码。请注意,mysql中指定UTF-8编码是给出的是UTF8,而不是UTF-8。
这里每个格式都需要一个数据库URL。 数据库**URL是指向数据库的地址。**制定数据库URL是建立连接相关联的大多数错误问题发生的地方。各数据库对应的URL如下所示:
假设我们现在需要连接MySQL数据库,格式为:jdbc:mysql://hostname:port/datebaseName。我们需要的信息是hostname主机名和端口号,一般默认为localHost:3306;还需要datebaseName数据库名,假设为mydb;当然还有URL格式未包含的也是必须的信息:连接数据库的用户名和密码,假设为root和123456。那么就有URL:
-
1 String url = "jdbc:mysql//localhost:3306/mydb";
下面分别使用四种方法来实现:
-
使用一个URL作为参数的方式:需要将username+password以参数的形式放到URL中,但是每种数据库的放置都不太相同
//连接mysql的纯URL String url = "jdbc:mysql//localhost:3306/mydb?username=root&password=123456"; Connection conn = DriverManager.getConnection(url,p); //连接Oracle的纯URL String url = "jdbc:oracle:thin:root/123456@192.0.0.10:1521:mydb";Connection conn = DriverManager.getConnection(url);
-
使用URL、properties作为参数的方式:即需要将username和password以键值对形式存放在properties对象中作为参数
//MySql String url = "jdbc:mysql//localhost:3306/mydb"; Properties p = new Properties(); p.put("username","root"); p.put("password","123456"); Connection conn = DriverManager.getConnection(url,p);
-
使用URL、username、password三个参数分开的方式(推荐)
String url = "jdbc:mysql//localhost:3306/mydb"; String username = "root"; String password = "123456"; Connection conn = DriverManager.getConnection(url,username,password);
-
利用
java.sql.Drivermanager
类中的getConnection()
方法与数据库建立连接。
Connection c = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?characterEncoding=UTF-8", "root", "123456");// 说明:
// DriverManager.getConnection(“jdbc:mysql://数据库地址:端口号/数据库名”,”用户名”, “密码”);
// 数据库服务端的IP地址:127.0.0.1 (这是本机,如果连接其他电脑上的数据库,需填写相应的IP地址)
// 数据库的端口号: 3306 (mysql专用端口号)
// 数据库名称 mydb(根据你自己数据库中的名称填写)
// 编码方式 UTF-8
// 账号 root
// 密码 123456(如果你在创建数据库的时候没有使用默认的账号和密码,请填写自己设置的账号和密码)
注意: Connection是与特定数据库连接回话的接口,使用的时候需要导包,而且必须在程序结束的时候将其关闭。getConnection方法也需要捕获SQLException异常。
因为在进行数据库的增删改查的时候都需要与数据库建立连接,所以可以在项目中将建立连接写成一个工具方法,用的时候直接调用即可:
/*** 取得数据库的连接* @return 一个数据库的连接*/
public static Connection getConnection(){Connection conn = null;try {//初始化驱动类com.mysql.jdbc.DriverClass.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?characterEncoding=UTF-8","root", "123456");//该类就在 mysql-connector-java-5.0.8-bin.jar中,如果忘记了第一个步骤的导包,就会抛出ClassNotFoundException} catch (ClassNotFoundException e) { e.printStackTrace();}catch (SQLException e) { e.printStackTrace();}return conn;}
第3步:获取数据库操作对象(Statement或者PreparedStatement)
SQL语句的执行对象按理说有Statement和PreparedStatement两个,但我们一般都不会去使用Statement,先看下两者的基本描述:
- Statement 是 Java 执行数据库操作的一个重要接口,用于在已经建立数据库连接的基础上,向数据库发送要执行的SQL语句。Statement对象,用于执行不带参数的简单SQL语句,即静态SQL语句。
- PreparedStatement 继承于Statement。实例包含已编译的 SQL 语句,这就是使语句“准备好”。包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN参数的值在 SQL 语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX() 方法来提供。
简言之,Statement执行静态SQL语句,而它的子类PreparedStatement执行预编译SQL,即可传入参数。两者相比之下,PreparedStatement有以下优势:
- **预编译处理,可动态执行SQL语句。**很明显,SQL语句的预编译,使用占位符?去代替未知数据,因而一个句子可以执行多种不同的SQL,而Statement需要重新书写SQL语句,笨重。
- 速度快,执行效率高。SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。使用预处理语句比普通的查询更快,因为它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完成了)。我们要利用预编译的特性
(1). 利用java.sql.Connection
创建用于执行SQL语句的Statement。
Statement stmt = connection.createStatement();
注意:使用Statement会引起sql注入问题,在实际开发中很少使用,用的的更多的是它的子类PreparedStatement
比如sql注入问题如下:
String sql = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"
验证需要用户输入用户名和密码,正确则执行查询语句(登录),但如果这样输入:
userName = "1' OR '1'='1";
passWord = "1' OR '1'='1";
那么执行语句就变成了:
1 String sql = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"
这样,where语句恒为真,就能实现无账号登录。此外便可能被恶意修改甚至删除数据表。然而使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,且占位符?不允许多值,只能填入一个值,因此就算参数中含有破坏性的指令,也不会被数据库所运行。
(2) .使用Connection对象的PreparedStatement(sql)方法进行获取:
String sql = "select * from user where name=? and ange=?";//预处理,需要我们先写好sql语句
PreparedStatement ps = conn.preparedStatement(sql);//conn是连接对象,参数为sql语句
第4步:执行sql语句(增删改查)
SQL语言共分为四大类:数据查询语言DQL,数据操纵语言DML,数据定义语言DDL,数据控制语言DCL
sql语句有增删查改等几种类型,所以执行方法有以下三种:
execute():
执行SQL语句,可以是任何种类的 SQL 语句。返回值是boolean类型。executeQuery()
:执行SQL语句查询,查询结果返回为**ResultSet
对象**。executeUpdate()
:执行更新语句。该语句必须是一个 SQL 数据操作语言(Data Manipulation Language,DML)语句,比如INSERT
、UPDATE
或DELETE
语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。返回值是int。
例如本例中的语句是查询语句,所以执行代码为:
-
//执行查询语句,并把结果集返回给集合ResultSet ResultSet rs = ps.executeQuery();
第5步:处理查询结果集(ResultSet)
如果返回值是boolean或者int很好处理,但如果是查询结果集ResultSet对象,一般使用while循环来处理:
ResultSet
对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next()
方法将光标移动到下一行;因为该方法在 ResultSet
对象没有下一行时返回 false
,所以可以在 while
循环中使用它来迭代结果集。另外,可以使用ResultSet对象的getXXX(int columnIndex)获得游标所在行指定列的值。原理如下图所示:
所以,本例的结果集处理如下:
1 while(rs.next()){
2 system.out.println(rs.getString(1));
3 system.out.println(rs.getInt(2));
4 }
第6步:释放资源
在JDBC程序结束之后,显式地需要关闭与数据库的所有连接以结束每个数据库会话。 但是,如果在编写程序中忘记了关闭也没有关系,Java的垃圾收集器将在清除过时的对象时也会关闭这些连接。
依靠垃圾收集,特别是数据库编程,是一个非常差的编程实践。所以应该要使用与连接对象关联的close()
方法关闭连接。要确保连接已关闭,可以将关闭连接的代码中编写在“finally
”块中。 一个finally
块总是会被执行,不管是否发生异常。
conn.close();
八.JDBCUtils工具类
因为传统JDBC的开发,注册驱动,获得连接,释放资源这些代码都是重复编写的。所以可以将重复的代码提取到一个类中来完成
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/*** JDBC的工具类* @author CodeAnime**/
public class JDBCUtils {private static final String driverClassName;private static final String url;private static final String username;private static final String password;static{driverClassName="com.mysql.jdbc.Driver";url="jdbc:mysql:///web_test3";username="root";password="123456";}/*** 注册驱动的方法*/public static void loadDriver(){try {Class.forName(driverClassName);} catch (ClassNotFoundException e) {e.printStackTrace();}}/*** 获得连接的方法*/public static Connection getConnection(){Connection conn = null;try{// 将驱动一并注册:loadDriver();// 获得连接conn = DriverManager.getConnection(url,username, password);}catch(Exception e){e.printStackTrace();}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;}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;}}
}
测试案例:导入工具类,查询用户信息
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Test;
import com.xdr630.jdbc.utils.JDBCUtils;/*** JDBC工具类的测试* @author xdr**/
public class JDBCDemo3 {@Test/*** 查询操作:使用工具类*/public void demo1(){Connection conn = null;Statement stmt = null;ResultSet rs = null;try{// 获得连接:conn = JDBCUtils.getConnection();// 创建执行SQL语句的对象:stmt = conn.createStatement();// 编写SQL:String sql = "select * from user";// 执行查询:rs = stmt.executeQuery(sql);// 遍历结果集:while(rs.next()){System.out.println(rs.getInt("id")+" "+rs.getString("username")+" "+rs.getString("password"));}}catch(Exception e){e.printStackTrace();}finally{// 释放资源:JDBCUtils.release(rs, stmt, conn);}}
}
另一种写法:
jdbc.properties属性文件
注意:属性配置文件必须以properties结尾,且存储在src源目录下
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
user=root
password=123456
JDBCUtils.java工具类
public class JDBCUtil(){//类文件属性,可以在类文件所有的方法中使用private Connection conn = null; private PreparedStatement ps = null;/*** 在当前类文件第一次被加载到JVM时,JVM将会自动调用当前类文件静态语句块*/static {//1. 注册数据库服务器提供的Driver接口实现类try{ResourceBundle bundle = ResourceBundle.getBundle("jdbc");String driver = bundle.getString("driver");Class.forName(driver);}catch(ClassNotFoundException e){e.printStackTrace();}}/*** 封装Connection对象创建细节* @return 数据库连接的对象*///这里我们选择抛出异常,而不是自行内部处理。是让调用此方法的人知道出现异常时的控制台出现的异常信息public Connection creatConnection() throws SQLException {//采用资源绑定器来绑定属性配置文件ResourceBundle bundle = ResourceBundle.getBundle("jdbc");String url = bundle.getString("url");String username = bundle.getString("username");String password = bundle.getString("password");//2. 创建一个连接通道交给Connection接口的实例对象[Connection]管理conn = DriverManager.getConnection(url,username,password);return conn;}/*** 封装PreparedStatement对象创建细节* @param sql sql语句* @return */public PreparedStatement createStatement(String sql) throws SQLException {Connection conn = creatConnection();//3. 创建一个交通工具交给 PreparedStatement 接口的实例对象[PreparedStatement]管理ps = conn.prepareStatement(sql);//4. 由交通工具在Java工程与数据库服务器之间进行传输,推送SQL命令并带回执行结果return ps;}/*** 封装PreparedStatement与Connection对象销毁细节*/public void close(){if (ps != null){try {ps.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 封装PreparedStatement与Connection与ResultSet对象销毁细节* @param rs 查询结果集*/public void close(ResultSet rs){if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}close();}
}
JDBCTest01.java测试
public class JDBCTest01 {public static void main(String[] args) {//创建JDBC工具类对象JdbcUtil util = new JdbcUtil();PreparedStatement ps = null;ResultSet rs = null;try {String sql = "select * from studnet where studentid = ?";//这里由于在工具类中是抛出异常的,那么在调用时再抛出异常给JVM虚拟机就显然不太合理了,此时我们选择try...catch...内部处理ps = util.createStatement(sql);ps.setString(5,"田七");rs = ps.executeQuery();while (rs.next()){System.out.println(rs.getInt("studentid") + rs.getString("studnetname"));}} catch (SQLException e) {e.printStackTrace();}finally {//在finally语句块中的语句必定执行util.close(rs);}}
}
参考文章:
-
作者 : Jungle_Rao
- 链接:https://blog.csdn.net/Jungle_Rao/article/details/81274720
-
作者 : Qiao_Zhi
- https://www.cnblogs.com/qlqwjy/p/8227665.html
-
作者 : 叫我玉弟大人
- https://www.cnblogs.com/jwyddr/p/11661096.html
-
作者 : 风大
- https://www.cnblogs.com/fzz9/p/8970210.html
-
作者 : newwaylau
- https://zhuanlan.zhihu.com/p/114709445