一、JDBC簡介
Sun公司為了簡化笼恰、統(tǒng)一對數(shù)據(jù)庫的操作,定義了一套java操作數(shù)據(jù)庫的規(guī)范囚衔,稱之為JDBC挖腰。
- 注意:JDBC只是一個規(guī)范,一套對數(shù)據(jù)庫操作的接口练湿,其是由sun公司定義的猴仑,所以我們在導入相關包時都是導入javaSE中的包,而不是導入相關的驅動中的包肥哎,那只是對sun公司JDBC的實現(xiàn)辽俗。
- JDBC主要由兩個包組成:
java.sql
和javax.sql
。開發(fā)jdbc應用需要以上兩個包的支持外篡诽,還需要導入相應的jdbc的數(shù)據(jù)庫實現(xiàn)(即驅動)崖飘。而java.sql
和javax.sql
已經(jīng)稱為javaSE的規(guī)范,所以現(xiàn)在不需要導入了杈女。只需要導入相關的驅動就可以了朱浴。
二、第一個JDBC程序
2.1搭建試驗環(huán)境
- 1.在mysql中創(chuàng)建一個庫达椰,并創(chuàng)建user表和插入表的數(shù)據(jù)翰蠢。
create database day14;
use day14;
create table user(
id int primary key auto_increment,
name varchar(40),
password varchar(40),
email varchar(60),
birthday date
)character set utf8 collate utf8_general_ci;
insert into user(name,password,email,birthday) values('zs','123456','zs@sina.com','1980-12-04');
insert into user(name,password,email,birthday) values('lisi','123456','lisi@sina.com','1981-12-04');
- 2.新建一個java工程(
day14
),并導入相關數(shù)據(jù)庫驅動啰劲。
驅動是:mysql-connector-java-5.1.37-bin.jar
Demo1.java
package cn.itcast.demo;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo1 {
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://localhost:3305/day14";
String username = "root";
String password = "walp1314";
//1.加載驅動
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
//2.獲取與數(shù)據(jù)庫的鏈接
Connection connection = DriverManager.getConnection(url, username, password);
//3.獲取用于向數(shù)據(jù)庫發(fā)送sql語句的statement對象
Statement st = connection.createStatement();
//4.向數(shù)據(jù)庫發(fā)送sql語句梁沧,并獲取代表結果集的ResultSet對象
String sql = "select id, name, password, email, birthday from user";
ResultSet result = st.executeQuery(sql);
//5.取出結果集的數(shù)據(jù)
while(result.next()){
System.out.println("id= " + result.getObject("id"));
System.out.println("name= " + result.getObject("name"));
}
//6.關閉鏈接,釋放資源
result.close();
st.close();
connection.close();
}
}
2.2程序詳解
2.2.1 DriverManager
- jdbc程序中的
DriverManager
用于加載驅動蝇裤,并創(chuàng)建與數(shù)據(jù)庫的連接廷支,這個API的常用方法:
DriverManager.registerDriver(new Driver());
DriverManager.getConnection(url,user,password);
- 注意:在實際開發(fā)中并不推薦采用
registerDriver
方法注冊驅動频鉴。原因有二:
一是通過Driver的源代碼可以看到,如果采用此種方式恋拍,會導致驅動程序注冊兩次垛孔,也就是在內存中會有兩個Driver對象。
二是程序依賴mysql的api施敢,脫離這個api的jar包似炎,程序將無法編譯,這樣不便于切換底層數(shù)據(jù)庫悯姊。
推薦方式:Class.forName("com.mysql.jdbc.Driver");
采用此種方式不會導致驅動對象在內存中重復出現(xiàn)羡藐,并且采用此種方式,程序僅僅需要一個字符串悯许,不需要依賴具體的驅動仆嗦,程序的靈活性更高。
同樣也不建議采用具體的驅動類型指向getConnection
方法返回的connection
對象先壕,比如:
conn = (com.mysql.jdbc.Connection) DriverManager.getConnection(url, username, password);
2.2.2 數(shù)據(jù)庫URL
- url用于標識數(shù)據(jù)庫的位置瘩扼,程序員通過url地址告訴jdbc程序連接哪個數(shù)據(jù)庫,url的寫法為:
jdbc:mysql://localhost:3305/day14?參數(shù)名:參數(shù)值
問號后面可以帶上編碼信息垃僚,如果不帶集绰,則根據(jù)數(shù)據(jù)庫的編碼自動指定。 - 常用數(shù)據(jù)庫URL地址的寫法:
Oracle寫法:jdbc:oracle:thin:@localhost:1521:sid
SqlServer寫法:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sid
MySql寫法:jdbc:mysql://localhost:3305/day14
- 常用屬性:
useUnicode=true&characterEncoding=UTF-8
2.2.3 Connection
- jdbc程序中的
Connection
用于代表數(shù)據(jù)庫的連接谆棺,Connection
是數(shù)據(jù)庫編程中最重要的一個對象栽燕,客戶端與數(shù)據(jù)庫所有交互都是通過Connection
對象完成的,這個對象的常用方法:
createStatement()
:創(chuàng)建向數(shù)據(jù)庫發(fā)送sql的statement對象改淑。
prepareStatement(sql)
:創(chuàng)建向數(shù)據(jù)庫發(fā)送預編譯sql的PrepareSatement對象碍岔。
prepareCall(sql)
:創(chuàng)建執(zhí)行存儲過程的callableStatement對象。
setAutoCommit(boolean autoCommit)
:設置事務是否自動提交朵夏。
commit()
:在鏈接上提交事務蔼啦。
rollback()
:在此鏈接上回滾事務。
2.2.4 Statement
- jdbc程序中的Statement對象用于向數(shù)據(jù)庫發(fā)送sql語句仰猖,Statement對象常用方法:
executeQuery(String sql)
:用于向數(shù)據(jù)發(fā)送查詢語句捏肢。
executeUpdate(String sql)
:用于向數(shù)據(jù)庫發(fā)送insert、update或delete語句
execute(String sql)
:用于向數(shù)據(jù)庫發(fā)送任意sql語句
addBatch(String sql)
:把多條sql語句放到一個批處理中饥侵。
executeBatch()
:向數(shù)據(jù)庫發(fā)送一批sql語句執(zhí)行鸵赫。 - 注意:這個addBatch是先將多條語句添加到Statement對象中,然后使用executeBatch方法一起執(zhí)行爆捞。
2.2.5 ResultSet
- jdbc程序中的ResultSet用于代表sql語句的執(zhí)行結果奉瘤。ResultSet封裝執(zhí)行結果時勾拉,采用的類似于表格的方式煮甥。ResultSet對象維護了一個指向表格數(shù)據(jù)行的游標盗温,初始的時候,游標在第一行之前成肘,調用
ResultSet.next()
方法卖局,可以使游標指向具體的數(shù)據(jù)行,進行調用方法獲取該行的數(shù)據(jù)双霍。 - ResultSet既然是用于封裝執(zhí)行結果的砚偶,所以該對象提供的都是用于獲取數(shù)據(jù)的get方法:
獲取任意類型的數(shù)據(jù)
getObject(int index)
getObject(string columnName)
獲取指定類型的數(shù)據(jù)
getString(int index)
getString(String columnName)
- 提問:數(shù)據(jù)庫中列的類型是varchar,獲取該列的數(shù)據(jù)調用什么方法洒闸?int類型呢染坯?bigint類型呢?Boolean類型丘逸?
常用數(shù)據(jù)類型轉換表
sql類型 | jdbc對應的方法 | 返回類型 |
---|---|---|
BIT(1) BIT(N) | getBoolean getBytes() | boolean byte[] |
TINYINT | getByte() | byte |
SMALLINT | getShort() | short |
INT | getInt() | int |
BIGINT | getLong() | long |
CHAR,VARCHAR,LONGVARCHAR | getString() | String |
TEXT(clob) BLOB | getClob() getBlob() | Clob Blob |
DATE | getDate() | java.sql.Date |
TIME | getTime() | java.sql.Time |
TIMESTAMP | getTimestamp() | java.sql.Timestamp |
- ResultSet還提供了對結果進行滾動的方法:
next()
:移動到下一行
previous()
:移動到前一行
absolute(int row)
:移動到指定行
beforeFirst()
:移動resultSet的最前面单鹿。
afterLast()
:移動到resultSet的最后面。
注意:開始時指向表頭深纲,使用一次next才指向第一條數(shù)據(jù)仲锄,而使用afterLast方法之后指向表尾,這樣使用previous方法才執(zhí)行最后一條數(shù)據(jù)湃鹊。
2.2.6 釋放資源
- jdbc程序運行完后儒喊,切記要釋放程序在運行過程中,創(chuàng)建的那些與數(shù)據(jù)庫進行交互的對象币呵,這些對象通常是ResultSet怀愧、Statement和Connection對象。
- 特別是Connection對象余赢,它是非常稀有的資源掸驱,用完后必須馬上釋放,如果不能及時没佑、正確的關閉毕贼,很容易導致宕機。其使用原則是盡量晚創(chuàng)建蛤奢,盡量早的釋放鬼癣。
- 為確保資源釋放代碼能運行,資源釋放代碼也一定要放在finally語句中啤贩。
對上面例子進行修正:Demo.java
package cn.itcast.demo;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Demo2 {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
String url = "jdbc:mysql://localhost:3305/day14";
String username = "root";
String password = "walp1314";
Connection connection = null;
Statement statement = null;
ResultSet result = null;
try{
//1.加載驅動(推薦)
Class.forName("com.mysql.jdbc.Driver");
//2.獲取與數(shù)據(jù)庫的鏈接
connection = DriverManager.getConnection(url, username, password);
//3.獲取用于向數(shù)據(jù)庫發(fā)送sql語句的statement對象
statement = connection.createStatement();
//4.向數(shù)據(jù)庫發(fā)送sql語句待秃,并獲取代表結果集的ResultSet對象
String sql = "select id, name, password, email, birthday from user";
result = statement.executeQuery(sql);
//5.取出結果集的數(shù)據(jù)
while(result.next()){
System.out.println("id= " + result.getObject("id"));
System.out.println("name= " + result.getObject("name"));
}
}finally{
//6.關閉鏈接,釋放資源
//方式一:
/*if(result != null){
try {
result.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}*/
//方式二:
if(result != null){
try {
result.close();
} catch (Exception e) {
e.printStackTrace();
}finally{
if(statement != null){
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}finally{
if(connection != null){
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
}
}
}
說明:
- 我們在使用jdbc的時候必須要保證最后資源能夠釋放痹屹,不管前面程序是否運行正確章郁。于是我們將資源釋放的代碼放在finally塊中,這樣就保證了不管發(fā)生什么情況下資源都會被釋放。上面我們提供了兩種釋放資源的方式暖庄,因為每一個關閉語句都可能會產生異常聊替,所以關閉比較麻煩。
- finally塊中的語句在JVM(exit(1))或是死循環(huán)時是不會執(zhí)行的培廓,其他情況下都會執(zhí)行惹悄,同時注意是在return語句之前執(zhí)行。
三肩钠、使用JDBC對數(shù)據(jù)庫進行CRUD
從之前的例子中我們可以發(fā)現(xiàn)在每次使用數(shù)據(jù)庫都需要鏈接數(shù)據(jù)庫和釋放資源泣港,我們可以將這些重復的代碼統(tǒng)一放在一個工具類中。同時對于數(shù)據(jù)庫中需要用到的一些資源价匠,比如url当纱、用戶名、密碼等我們直接寫死在程序中也不便于日后維護踩窖,于是我們將這些資源放在一個配置文件中惫东。
src/db.properties
### MySQL
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3305/day14
username=root
password=walp1314
### Oracle
#driver=oracle.jdbc.driver.OracleDriver
#url=jdbc:oracle:thin:@localhost:1521:orcl
#username=root
#password=walp1314
JdbcUtils.java
package cn.itcast.utils;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try{
//獲取資源文件,這里我們的資源文件放在src下
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
}catch(Exception e){
throw new ExceptionInInitializerError(e);
}
}
//獲得數(shù)據(jù)庫鏈接
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url, username, password);
}
//釋放資源
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(st != null){
try {
st.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
測試:Demo3.java
package cn.itcast.demo;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.junit.Test;
import cn.itcast.utils.JdbcUtils;
public class Demo3 {
//數(shù)據(jù)庫插入
@Test
public void insert(){
Connection connection = null;
Statement statement = null;
ResultSet result = null;
try {
//通過工具類獲得數(shù)據(jù)庫的鏈接
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "insert into user(id, name, password, email, birthday) "
+ "values(4, 'xxx', '12', 'xx@sina.com', '1998-09-09')";
//下面的返回值表示影響了數(shù)據(jù)庫多少行
int num = statement.executeUpdate(sql);
if(num > 0){
System.out.println("插入成功");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//通過工具類釋放資源
JdbcUtils.release(connection, statement, result);
}
}
//刪除操作
@Test
public void delete(){
Connection connection = null;
Statement statement = null;
ResultSet result = null;
try {
//通過工具類獲得數(shù)據(jù)庫的鏈接
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "delete from user where id = 4";
int num = statement.executeUpdate(sql);
if(num > 0){
System.out.println("刪除成功");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//通過工具類釋放資源
JdbcUtils.release(connection, statement, result);
}
}
//修改操作
@Test
public void update(){
Connection connection = null;
Statement statement = null;
ResultSet result = null;
try {
//通過工具類獲得數(shù)據(jù)庫的鏈接
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "update user set name='tom', email='tom@sina.com' where id = 2";
int num = statement.executeUpdate(sql);
if(num > 0){
System.out.println("修改成功");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//通過工具類釋放資源
JdbcUtils.release(connection, statement, result);
}
}
//查找操作
@Test
public void find(){
Connection connection = null;
Statement statement = null;
ResultSet result = null;
try {
//通過工具類獲得數(shù)據(jù)庫的鏈接
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "select * from user where id = 1";
result = statement.executeQuery(sql);
if(result.next()){
System.out.println(result.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//通過工具類釋放資源
JdbcUtils.release(connection, statement, result);
}
}
}
注意:在以后的開發(fā)中我們不會再使用Statement這個對象毙石,而是使用其子類PreparedStatement廉沮,同時這個對象可以防范sql注入,相關使用方法請看筆記17.2 徐矩。