二刷jdbc
作者小結:從第一次大概幾天快速刷完jdbc,到如今的二刷,才發現自己對jdbc的理解有點太淺,到學習javaweb是創建資料庫層時的迷茫,到現在對這種設計模式的理解,我深有體會到了:實打實走好每一步的必要性!這篇筆記較為完整的展示了jdbc的發展脈絡,從原理到手動封裝,再到第三方庫,循序漸進,
## jdbc概述
- jdbc為訪問不同的資料庫提供了統一的介面,
- java程式員使用jdbc,可以連接任何提供了jdbc驅動程式的資料庫系統,從而完成對資料庫的各種操作
- jdbc的基本原理圖

java程式通過制定一些介面,讓資料庫廠商實作這些介面
*************************模擬************************
//java制定的資料庫介面
Interface jdbcInterface{
//連接
public Object getConnection();
//crud
public void crud();
//關閉連接
public void close();
}
//mysql廠商繼承介面從而實作這些方法
public class MysqlJdbcImpl implements jdbcInterface{
@Override
public Object getConnetion() {
System.out.println("mysql的實作");
return null;
}
@Override
public void crud() {
}
@Override
public void close() {
}
}
java程式使用
public class testjdbc{
public static void main(String[] args) {
//通過介面來呼叫實作類[動態系結]
jdbcInterface mysqlImpl = new mysqlImpl();
//通過介面來呼叫實作類
mysqlImpl.getConnetion();
mysqlImpl.crud();
mysqlImpl.close();
}
}
通過介面來呼叫實作類的意義:(思考介面編程的好處)
? 當用戶用其他資料庫廠商的實作類時只需動態系結其他資料庫實作類
? 例如上方:jdbcInterface mysqlImpl = new DB2Impl();


jdbc程式撰寫步驟
- 注冊驅動-加載Driver類
- 獲取連接-得到Connection
- 執行增刪改查-發送sql給相應的資料庫執行
- 釋放資源-關閉相關的連接
簡單的步驟:
-
匯入對應的jar包,即驅動檔案
-
注冊驅動
Derver driver=new new com.mysql.jdbc.Driver();
-->簡寫為 Derver driver=new driver();
-
String url 解讀
String url="jdbc::mysql://ip:port/資料庫名"
jdbc::mysql://規定好的,表示一個協議,通過jdbc的方式連接mysql,
ip 連接到的主機名稱
3306 表示mysql監聽的埠
資料庫名 表示連接到mysql dbms的哪一個資料庫

? mysql的連接本質就是socket連接
-
將用戶名和密碼放入到properties物件中
Properties properties = new Properties(); properties.setProperty("user","book");//用戶 properties.setProperty("password","xxxx");//密碼 -
得到連接
Connection conn=driver.connect(url,properties); -
執行sql
String sql="select * from xxx";Statement用于執行sql陳述句
Statement statement=conn.createStatement(); int i=statement.excuteUpdate(sql); //i表示受影響的行數 -
關閉資源
statemen.close(); conn.close();不關閉資源造成的影響:會造成連接不到mysql

獲取資料庫連接的五種方式
1.方式一 獲取Driver實作類物件
Driver driver=new com.mysql.jdbc.Driver();
String url="jdbc:mysql://ip:port/資料庫名";
Properties properties=new Properties();
properties.setProperty("user","name");
properties.setProperty("password","xxxx");
Connection conn=driver.connect(url,properties);
通過new了一個第三方的driver,第三方的dirver 是靜態加載,靈活性不高,
2.方式二 使用反射機制,動態加載,
//使用反射加載Driver類
Class clazz=Class.forName("com.mysql.jdbc.Driver");
Driver driver= (Driver) clazz.newInstance();
String url="jdbc:mysql://ip:port/資料庫名";
Properties properties=new Properties();
properties.setProperty("user","name");
properties.setProperty("password","xxxx");
Connection conn=driver.connect(url,properties);
反射動態加載,更加靈活,減少依賴性,
3.方式三使用DriverManager進行統一管理
//使用反射加載Driver
Class clazz=Class.forName("com.mysql.jdbc.Driver");
Driver driver= (Driver) clazz.newInstance();
//創建url user password
String url="jdbc:mysql://ip:port/資料庫名";
String user="root";
String password="xxxx";
//注冊Driver驅動
DriverManager.registerDriver(driver);
Connection conn=DriverManager.getConnection(url,user,password);
DriverManagaer用于管理一組jdbc驅動程式的基本服務
4.方式四使用forName()自動完成注冊驅動,簡化代碼--推薦使用
//使用反射加載Driver
Class clazz=Class.forName("com.mysql.jdbc.Driver");
//創建url user password
String url="jdbc:mysql://ip:port/資料庫名";
String user="root";
String password="xxxx";
Connection conn=DriverManager.getConnection(url,user,password);
Class.forName 在加載Driver類時自動完成了注冊

tip:沒用顯示呼叫Class.forName("com.mysql.jdbc.Driver")仍然可以拿到資料庫的連接,建議寫上,更加明確


5.方式五通過寫組態檔,讓連接更加靈活
//通過Peoperties物件獲取組態檔的資訊
Properties properties=new Properties();
properties.load(new FileInputStream("com\\mysql.properties"));
//通過key獲取相關的值
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
Class.forName(driver);
DriverManager.getConnection(url,user,password);
在方式四的基礎上,增加組態檔,讓連接更加靈活,
組態檔:
#key=value
user=root
password=xxx
url=jdbc:mysql://ip:port/資料庫名
driver=com.mysql.jdbc.Driver
ResultSet結果集--底層(?)
概述:表示資料庫結果集的資料表,通常通過查詢資料庫的陳述句生成,ResultSet物件保持一個游標指向其當前的資料行,最初游標位于第一行,next方法將游標移動到下一行,并且由于在ResultSet物件中沒有更多行時回傳false,類似于迭代器,
//得到Statemen
Statement statement = connection.createStatement();
//sql陳述句
String sql="SELECT * FROM xxx";
//執行給定的sql陳述句,該陳述句回傳單個ResultSet物件即為一張表
java.sql.ResultSet resultSet = statement.executeQuery(sql);
//回圈取出
while(resultSet.next()){//讓游標向后移動,如果沒有更多行,則回傳false
resultSet.getInt(1);//獲取改行的第一列資料
resultSet.getString(2);//獲取該行第二列
}
//關閉資源
resultSet.close();
statement.close();
connection.close();
statement--存在sql注入問題
概述:用于執行靜態的sql陳述句并回傳其生成的結果的物件
statement是一個介面需要不同的資料庫廠商實作
解決方案:使用preperdStatement
代碼實作:
//得到Statemen
Statement statement = connection.createStatement();
//sql陳述句xx
String sql="SELECT * FROM xxx";
//執行給定的sql陳述句,該陳述句回傳單個ResultSet物件即為一張表
java.sql.ResultSet resultSet = statement.executeQuery(sql);
如果將用戶輸入改成next()也可以防止sql注入,next遇到空格會停止,
PreperdStatement
概述:預處理Statement,是一個介面,
用法:
1.PreperdStatement執行的sql陳述句中的引數用問號(?)來表示,呼叫PreperdStatement物件的setXXX()方法來設定這些引數,setXXX()方法有兩個引數,第一個引數是要設定的sql陳述句中的引數的索引(即第幾個問號),第二個設定的是引數的值,
2.呼叫executeQuery(),回傳ResultSet物件
3.呼叫excuteUpdate(),執行crud,回傳影響行數
預處理好處:
? 不在使用+拼接sql陳述句,減少語法錯誤,有效解決了sql注入問題,減少了編譯次數,效率較高,
預處理就是再執行sql之前就已經完成對sql的賦值,
代碼實作:
//sql陳述句xx,設定問號
String sql="SELECT * FROM xxx WHERE name=?and password=?";
//得到PreparedStatemen
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//給問號賦值
preparedStatement.setString(1,user_name);
preparedStatement.setString(2,user_pass);
//執行注意執行的時候不需要再填sql
preparedStatement.executeQuery();
添加記錄dml
String sql="INSERT INTO xxx VALUES(?,?)";
API小結


封裝Utils類
簡介:在jdbc操作中,獲取資料庫連接和釋放資源是經常使用到的可以將其封裝為JDBC連接的工具類Utils,

使用步驟:
- 定義相關的屬性(4個),因為只需要一份,所以用static修飾
- 在static代碼塊初始化
- 通過組態檔讀取相關的屬性值
- 寫連接函式,推薦使用DriverManager
- 寫釋放資源函式
代碼實作:
//定義相關的屬性(4個),因為只需要一份,所以用static修飾
private static String user;//用戶名
private static String password;//密碼
private static String url;//資料庫url
private static String driver;//驅動名
//在static代碼塊初始化
static{
try {
Properties properties=new Properties();
properties.load(new FileInputStream("com\\mysql.properties"));
//讀取相關的屬性值
user = properties.getProperty("user");
password=properties.getProperty("password");
url=properties.getProperty("url");
driver=properties.getProperty("driver");
} catch (IOException e) {
//在實際開發中,常常轉為運行例外拋出
//將編譯例外轉為運行例外,呼叫者可以選擇捕獲該例外,也可以選擇默認處理該例外,比較方便,
throw new RuntimeException(e);
}
}
//連接資料庫,回傳Connection
public static Connection getConnection() throws SQLException, ClassNotFoundException {
Class.forName(driver);
return DriverManager.getConnection(url,user,password);
}
//關閉相應資源
/*
可能關閉的資源
1.ResultSet結果集
2.Statement和preparedStatement
3.connection
4.如果需要關閉資源,則傳入物件,否則傳入null
*/
//用statement來接受因為statement是preparedStatement的父介面,都可以接收
/*
當一個物件被當作引數傳遞到一個方法后,此方法可改變這個物件的屬性,并可回傳變化后的結果,那么這里到底是值傳遞還是參考傳遞?
Java 編程語言只有值傳遞引數,當一個物件實體作為一個引數被傳遞到方法中時,引數的值就是對該物件的參考,物件的內容可以在被呼叫的方法中改變,但物件的參考是永遠不會改變的,
*/
public static void close(ResultSet resultSet, Statement state,Connection conn) throws SQLException {
if(resultSet!=null){
resultSet.close();
}
if(state!=null){
state.close();
}
if(conn!=null){
state.close();
}
}
實際開發程序中例外處理:
在實際開發中,常常轉為運行例外拋出,將編譯例外轉為運行例外,呼叫者可以選擇捕獲該例外,也可以選擇默認處理該例外,比較方便,throw new RuntimeException(e);
Java是值傳遞:
Java 編程語言只有值傳遞引數,當一個物件實體作為一個引數被傳遞到方法中時,引數的值就是對該物件的參考,物件的內容可以在被呼叫的方法中改變,但物件的參考是永遠不會改變的,
Utils使用
使用步驟:
- 得到連接,
- 組織一個sql陳述句,
- 創建一個PreparedStatement物件,
- 執行sql陳述句,
- 釋放資源呼叫close(),
代碼實作:
public class use_utils {
public void use_ut() throws SQLException {
Connection conn=null;
String sql="SELECT * FROM xxx";
PreparedStatement preparedStatement=null;
try {
//得到連接
conn=jdbcutils.getConnection();
//創建PreparedStaement
preparedStatement= conn.prepareStatement(sql);
preparedStatement.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
jdbcutils.close(null,preparedStatement,conn);
}
}
}
事務
概述:Jdbc程式中當一個Connection物件創建時,默認情況下是自動提交事務,不能回滾,并且jdbc程式中為了讓多個SQL陳述句作為一個整體執行,需要使用事務,呼叫Connection的setAutoCommit(false)可以取消自動提交事務,當所有的sql陳述句都執行后,呼叫Commit()方法即可提交事務,在其中某個操作失敗或出現例外時,呼叫rollback()方法即可回滾事務,
默認情況下,Connection物件是自動提交的,
應用實體:經典的轉賬業務,
代碼實作: "未使用事務"
Connection conn=null;
String sql="update account set balance=balance-100 where id=1";
String sql2="update account set balance=balance+100 where id=2";
PreparedStatement preparedStatement=null;
try {
//得到連接
conn=jdbcutils.getConnection();
//創建PreparedStaement
preparedStatement= conn.prepareStatement(sql);
preparedStatement.executeQuery();//執行第一條sql
int i=1/0;//拋出例外
preparedStatement=conn.prepareStatement(sql2);
preparedStatement.executeQuery();//執行第二條SQL
}
上述代碼,在執行sql時如果沒用開啟事務,會造成第一條sql執行成功,而第二條sql未執行便被捕獲例外,在轉賬問題方面就會出現問題,
代碼實作: 開啟事務
//得到連接
conn=jdbcutils.getConnection() //創建PreparedStaement
preparedStatement= conn.prepareStatement(sql);
/*得到連接后將conn設定為不自動提交*/
conn.setAutoCommit(false);
preparedStatement.executeQuery();//執行第一條sql
int i=1/0;//拋出例外
preparedStatement=conn.prepareStatement(sql2);
preparedStatement.executeQuery();//執行第二條SQL
/*在catch中即可處理例外,撤消先前已經執行的sql*/
/*即回滾*/
catch(Exception e){
conn.rollback();
}
rollback默認回滾到事務開啟的地方,
批處理
概述:當需要成批插入或者更新資料時,可以采用Java批量更新機制,這一機制允許將多條陳述句一次性提交給資料庫批量處理,
批處理步驟:
-
如果使用批處理時,需要在url中添加引數:
?rewriteBatchedStatements=true
-
addBatch():添加需要批量處理的SQL陳述句或引數
-
excuteBatch():執行批量處理的陳述句
-
clearBatch():清空批處理包的陳述句
批處理優勢:批處理往往和PreparedStatement一起搭配使用,既可以減少編譯次數,又減少運行次數,讓效率提高,
代碼實作:
? 傳統代碼
public void nobatch() throws SQLException, ClassNotFoundException {
Connection connection = jdbcutils.getConnection();
String sql="insert into xxx values(null,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"tom"+i);
preparedStatement.setString(2,"xxx");
preparedStatement.executeUpdate();
}
//關閉連接
jdbcutils.close(null,preparedStatement,connection);
}
? 批處理代碼
public void batch_() throws SQLException, ClassNotFoundException {
Connection connection = jdbcutils.getConnection();
String sql="insert into xxx values(null,?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < 5000; i++) {
preparedStatement.setString(1,"tom"+i);
preparedStatement.setString(2,"xxx");
/*preparedStatement.executeUpdate();*/
//將sql陳述句加入到批處理包中 ->
preparedStatement.addBatch();
//當有1000條資料時,在批量執行
if((i+1)%1000==0){
//滿1000條
preparedStatement.executeBatch();
//清空
preparedStatement.clearBatch();
}
}
//關閉連接
jdbcutils.close(null,preparedStatement,connection);
}
關鍵代碼:
preparedStatement.addBatch();
//當有1000條資料時,在批量執行
if((i+1)%1000==0){
//滿1000條
preparedStatement.executeBatch();
//清空
preparedStatement.clearBatch();
}
注:源代碼未了解!
資料庫連接池
概述:傳統方式連接資料庫過多,由于沒用的連接資源未被及時斷開會造成,連接不上數s據庫,資料庫連接池就誕生了,資料庫連接池可以合理分配連接資源,
實作方式:
1.預先在緩沖池中放入一定數量的連接,當需要建立資料庫連接時,只需要從 "緩沖池"中取出一個,使用完畢之后再放回去,
2.資料庫連接池負責分配,管理和釋放資料庫連接,它允許應用程式重復使用一個現有的資料庫連接,而不是重寫建立一個
3.當應用程式向連接池請求的連接數超過最大連接數量時,這些請求將被加入到等待佇列中,


資料庫連接池種類:
1.Jdbc的資料庫連接池使用java.sql.dateSource來表示,DateSource只是一個介面,該介面由第三方提供實作,
2.C3P0, DBCP, Proxool, BoneCP, Druid
C3P0
實作步驟:
? 一、傳統方式
1.創建一個資料源物件,
2.通過組態檔獲取相關的資訊user,url...
3.給資料源ComboPooledDataSource(c3p0)設定相關的引數url,user...setInitialPoolSize()方法設定初始化連接數,setMaxPoolSize()方法設定最大連接數,
4.得到連接,
5.關閉連接--即放回到連接池中,
? 二、使用組態檔模板
概述:c3p0設計者提供了一個xml檔案,方便配置
1.將C3P0提供的組態檔 c3p0.config.xml拷貝到src目錄下
2.創建一個資料源物件,引數即為c3p0.config.xml檔案中的
3.得到連接
4.關閉連接--即放回到連接池中,
? C3P0:

組態檔:c3p0.config.xml
<c3p0-config>
<!-- 資料源名稱代表連接池-->
<name-config name="xxx">
<!-- 連接引數 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/web</property>
<property name="user">root</property>
<property name="password">ROOT</property>
<!-- 連接池引數 -->
<!-- 每次增長連接池可供連接數 -->
<property name="acquireIncrement">10</property>
<!-- 初始連接數 -->
<property name="initialPoolSize">5</property>
<!-- 最大連接數 -->
<property name="maxPoolSize">10</property>
<!-- 最大等待時間 -->
<property name="checkoutTimeout">2000</property>
<!-- 最大空閑回收時間 -->
<property name="maxIdleTime">1000</property>
</c3p0-config>
代碼實作:
? 傳統方式
public void testc3p0() throws IOException, PropertyVetoException, SQLException {
//1.創建一個資料源物件
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
//2.通過組態檔獲取相關的資訊
Properties properties=new Properties();
properties.load(new FileInputStream("com\\mysql.properties"));
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//給資料源 comboPooledDataSource設定相關的引數,
//我們連接的管理是由comboPooledDataSource來管理的,
comboPooledDataSource.setDriverClass(driver);
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(password);
comboPooledDataSource.setJdbcUrl(url);
//設定連接數--初始化連接數
comboPooledDataSource.setInitialPoolSize(10);
//最大連接數
comboPooledDataSource.setMaxPoolSize(50);
Connection connection = comboPooledDataSource.getConnection();//這個方法就是從DateSource介面實作的
connection.close();
}
? xml組態檔的方式
public void test04() throws SQLException {
//1.將組態檔匯入src目錄下
//2.創建一個資料源物件,引數即為c3p0.config.xml檔案中的 <name-config name="nihao">
////資料源會根據資料源名稱讀取xml檔案中的內容
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("nihao");
Connection connection = comboPooledDataSource.getConnection();
connection.close();
}
注:資料源名稱不能寫錯,并且xml檔案的名稱是固定的,資料源會根據資料源名稱讀取xml檔案中的內容,自動完成配置,
Druid
概述:
Druid連接池是阿里實作的,獲取連接的速度比較快,
實作步驟:
1.添加jar包,和properties組態檔,將組態檔拷貝到專案的src目錄下,
? driverClassName 底層用這個欄位來讀取資料庫驅動,
? minIdle 空閑時候的連接數量
2.創建Properties物件來讀取組態檔
3.創建一個指定引數的資料庫連接池
4.得到連接
5.釋放連接
代碼實作:
public void druidx() throws Exception {
/*
1.添加jar包,和properties組態檔,將組態檔拷貝到專案的src目錄下,
driverClassName 底層用這個欄位來讀取資料庫驅動,
minIdle 空閑時候的連接數量
2.創建Properties物件來讀取組態檔
*/
Properties properties = new Properties();
properties.load(new FileInputStream("src\\druid.properties"));
//創建一個指定引數的資料庫連接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//得到連接
Connection connection = dataSource.getConnection();
connection.close();
}
組態檔: druid.properties
driverClassName=com.mysql.jdbc.Driver //驅動加載
url=jdbc:mysql://127.0.0.1:3306/student?characterEncoding=utf-8 //注冊驅動
username=root //連接資料庫的用戶名
password=sjw58586 //連接資料庫的密碼,
filters=stat //屬性型別的字串,通過別名的方式配置擴展插件, 監控統計用的stat 日志用log4j 防御sql注入:wall
initialSize=2 //初始化時池中建立的物理連接個數,
maxActive=300 //最大的可活躍的連接池數量
maxWait=60000 //獲取連接時最大等待時間,單位毫秒,超過連接就會失效,配置了maxWait之后,預設啟用公平鎖,并發效率會有所下降, 如果需要可以通過配置useUnfairLock屬性為true使用非公平鎖,
timeBetweenEvictionRunsMillis=60000 // 連接回收器的運行周期時間,時間到了清理池中空閑的連接,testWhileIdle根據這個判斷
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1 //用來檢測連接是否有效的sql,要求是一個查詢陳述句,
testWhileIdle=true //建議配置為true,不影響性能,并且保證安全性, 申請連接的時候檢測,如果空閑時間大于timeBetweenEvictionRunsMillis, 執行validationQuery檢測連接是否有效,
testOnBorrow=false //申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能,設定為false
testOnReturn=false //歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能,設定為flase
poolPreparedStatements=false //是否快取preparedStatement,也就是PSCache,
maxPoolPreparedStatementPerConnectionSize=200 // 池中能夠緩沖的preparedStatements陳述句數量
將Jdbc工具類改成druid實作
代碼實作:
private static DataSource ds;
//在靜態代碼塊完成ds初始化
static{
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
ds= DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
new RuntimeException(e);
}
}
//得到連接方法
public Connection getConnection() throws SQLException {
return ds.getConnection();
}
//釋放資源,Connection放回連接池,此是的close是資料庫連接池實作的close方法,
public void close(ResultSet rs, Connection conn, Statement st) throws SQLException {
if(rs!=null){
rs.close();
}
if (conn!=null){
conn.close();
}
if(st!=null){
st.close();
}
}
注:此時呼叫的close方法,是連接池實作的close()方法并不會真正的關閉連接,而是將連接放回到資料庫連接池,
Bean,Domain,POJO
問題引出:
1.java程式使用Connection連接,Connection和ResultSet關聯,當關閉Connection無法再使用ResultSet,
2.如果一個程式回傳ResultSet物件,這是已經關閉Connection,仍然無法拿到結果集物件,導致結果集只能使用一次,
解決方案:
寫一個Java類---->常被叫做Bean,Domain,POJO,該類中有查詢到的表中的欄位屬性,讓一個Actor物件對應查詢到的一條記錄,將結果集封裝到ArrayList中,

ApachDBUtils
概述:面對ResultSet問題,ApachDBUtils工具類完美解決了這個問題,
? 傳統方式解決(土方法封裝)
實作步驟:
1.新建一個POJO類,用于封裝查詢到的表的記錄,
2.類中定義表中相對應的欄位,建構式,setter,getter方法,
? 注:一定要給一個默認建構式[反射需要],
3.在java程式中用ArrayList 來存貯資料,
4.在ResultSet遍歷的時候,封裝資料,存盤到集合中,
代碼實作:
Actor---POJO
import java.util.Date;
public class Actor {//POJO
//和表的欄位相對應
//細節建議用Integer裝箱
private Integer id;
private String name;
private String sex;
//Date用util包下的,
private Date bornDate;
private String phone;
//一定要給一個無參構造器[反射會需要]
public Actor(){
}
public Actor(Integer id, String name, String sex, Date bornDate, String phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.bornDate = bornDate;
this.phone = phone;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setBornDate(Date bornDate) {
this.bornDate = bornDate;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public Date getBornDate() {
return bornDate;
}
public String getPhone() {
return phone;
}
}
java程式:
public void testSelecttoArraylist() throws Exception {
Properties properties=new Properties();
properties.load(new FileInputStream("com\\mysql.properties"));
//通過key獲取相關的值
String user_name;
String user_pass;
Scanner scanner = new Scanner(System.in);
user_name=scanner.nextLine();
user_pass=scanner.nextLine();
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
//創建ArrayList物件,存放Actor物件
ArrayList<Actor> list=new ArrayList<>();
//注冊驅動
Class.forName(driver);
//得到連接
Connection connection = DriverManager.getConnection(url, user, password);
//sql陳述句xx,設定問號
String sql="SELECT * FROM xxx WHERE name=?and password=?";
//得到PreparedStatemen
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//給問號賦值
preparedStatement.setString(1,user_name);
preparedStatement.setString(2,user_pass);
//執行給定的sql陳述句,該陳述句回傳單個ResultSet物件即為一張表
java.sql.ResultSet resultSet = preparedStatement.executeQuery(sql);
//回圈取出
while(resultSet.next()){//讓游標向后移動,如果沒有更多行,則回傳false
int id=resultSet.getInt("id");
String name = resultSet.getString("name");
String sex = resultSet.getString("sex");
Date borndate = resultSet.getDate("borndate");
String phone = resultSet.getString("phone");
//把得到的resultSet記錄封裝到Actor物件,放入到list集合,
list.add(new Actor(id,name,sex,borndate,phone));
}
//關閉資源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
? ApachDBUtils解決
概述:commons-dbutils是Apache組織提供的一個開源的JDBC工具類別庫,它是對JDBC的封裝,使用dbutils能極大簡化jdbc編碼的作業量,
dbutils常用類和介面:
- QueryRunner類:該類封裝了SQl的執行,是執行緒安全的,可以實作增刪改查,批處理,
- ResultSetHandler介面:該介面用于處理java.sql.ResultSet,將資料按要求轉換為另一種形式,
ArrayHandler: 把結果集中的第一行資料轉成物件陣列
ArrayListHandler: 把結果集中的每一行資料都轉成一個陣列,再存放到List中,
BeanHandler: 將結果集中的第一行資料封裝到一個對應的JavaBean實體中,
BeanListHandler: 將結果集中的每一行資料都封裝到一個對應的JavaBean: 實體中,存放到List里,
ColumnListHandler: 將結果集中某一列的資料存放到List中,
KeyedHandler(name): 將結果集中的每行資料都封裝到Map里,再把這些map再存到一個map里,其key為指定的key,
MapHandler: 將結果集中的第一行資料封裝到一個Map里,key是列名,vaue就是對應的值,
MapListHandler: 將結果集中的每一行資料都封裝到一個Map里,然后再存放到List
使用步驟:druid+dbutils
-
引入commons jar包
-
使用自己封裝的DruidUtils得到連接
-
創建QueryRunner
-
呼叫QueryRunner的query方法執行sql回傳ArrayList集合
注:sql陳述句也可以查詢部分列
BeanListHandler<>(Actor.class):在將ResultSet->Actor 物件->封裝到ArrayList
1: 傳給sql中的問號的,可以有多個后邊是可變引數Object...params
底層得到的ResultSet會在query執行后關閉,PreparedStatement也會自動關閉
引數串列 (connection, sql, new BeanListHandler<>(Actor.class), 1) -
關閉連接,
代碼實作:
public void querytest() throws SQLException {
//得到連接(druid)
Connection connection = DruidUtils.getConnection();
//使用DbUtils類和介面,引入相應的jar檔案,
//創建QueryRunner
QueryRunner queryRunner = new QueryRunner();
//QueryRunner就可以執行相關的方法,回傳ArrayList結果集
String sql="SELECT * FROM xxx WHERE id>=?";
//sql陳述句也可以查詢部分列
/*
queryRunner.query方法就是執行一個sql陳述句得到ResultSet,--封裝到ArrayList集合中
然后回傳集合,
引數Connection 連接
sql: 執行的sql陳述句
BeanListHandler<>(Actor.class):在將ResultSet->Actor 物件->封裝到ArrayList
底層使用反射機制獲取Actor屬性進行封裝
1: 傳給sql中的問號的,可以有多個后邊是可變引數Object...params
底層得到的ResultSet會在query執行后關閉,PreparedStatement也會自動關閉
* */
List<Actor> list = queryRunner.query(connection, sql, new BeanListHandler<>(Actor.class), 1);
DruidUtils.close(null,connection,null);
}
dbutils查詢單行記錄(單個物件)
解決方案:回傳單行記錄,單個物件使用的Hander是BeanHandler
new BeanHandler<>(Actor.class)
單行單列:回傳的是一個Object,物件使用Handers是ScalarHandler()
new ScalarHandler()
dbutils+druid實作crud
執行dml操作使用queryRunner.update()
回傳值是受影響的行數,
代碼實作:
public void testcrud() throws Exception{
Connection connection = DruidUtils.getConnection();
QueryRunner queryRunner = new QueryRunner();
String sql="update actor set name=? where id=?";
int affectedrows = queryRunner.update(connection, sql, "niuma", 4);
}
對應關系:
注: java中全部對應包裝類,

BasicDAO
問題分析:
dbutils和druid簡化了JDBC開發,但有不足
1.Sql陳述句是固定,不能通過引數傳入,通用性不好,需要進行該進,更方便執行crud
2.對于select操作,如果有回傳值,回傳值型別不能固定,需要使用泛型
3.將來的表很多,業務需求復雜,不可能只靠一個Java類完成
設計理念:各司其職,一張表對應一個DAO
DAO:訪問資料的物件


BasicDAO將所有的DAO共有的部分提取出來,讓子類去繼承簡化代碼,
設計步驟:
1.第一個包 放utils工具類
2.第二個包 javaBean/domain
3.第三個包 放xxxDAO和BasicDAO
4.第四個包 寫測驗類
目錄結構:

代碼:
BasicDAO:
package com.basic_.dao;
import com.basic_.utils.DruidUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import javax.management.Query;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
//開發basicDAO
//添加泛型將來是操作domain的
public class BasicDAO<T> {
private QueryRunner qr=new QueryRunner();
//開發通用的dml方法,針對任意的表
public int update(String sql,Object...prameters) throws SQLException {
Connection conn=null;
conn= DruidUtils.getConnection();
int affectedRows = qr.update(conn, sql, prameters);
return affectedRows;
}
//回傳多個物件(即查詢的結果是多行的),針對任意表
//sql陳述句可以有問號,占位符
//clazz傳入一個類的Class物件,底層通過反射實作domain 比如Actor.class
//prameters傳入問號具體的值,
public List<T> queryMulti(String sql, Class<T> clazz, Object...prameters) throws SQLException {
Connection conn=null;
conn=DruidUtils.getConnection();
List<T> list = qr.query(conn, sql, new BeanListHandler<>(clazz), prameters);
DruidUtils.close(null,conn,null);
return list;
}
//查詢單行結果通用方法 T可能是Actor..不同的表,
public T querySingle(String sql,Class<T> clazz,Object...prameters) throws SQLException {
Connection conn=null;
conn= DruidUtils.getConnection();
T rs = qr.query(conn, sql, new BeanHandler<>(clazz), prameters);
DruidUtils.close(null,conn,null);
return rs;
}
//查詢單行單列即回傳單值的方法
public Object queryScalar(String sql,Object...prameters) throws SQLException {
Connection conn=null;
conn= DruidUtils.getConnection();
Object rs = qr.query(conn, sql, new ScalarHandler(), prameters);
DruidUtils.close(null,conn,null);
return rs;
}
}
ActorDAO
public class ActorDAO extends BasicDAO<Actor>{
//Actor有BasicDAO 的方法
//根據業務需求,可以撰寫特有的方法
}
domain
public class Actor {//POJO
//和表的欄位相對應
//細節建議用Integer裝箱
private Integer id;
private String name;
private String sex;
//Date用util包下的,
private Date bornDate;
private String phone;
//一定要給一個無參構造器[反射會需要]
public Actor(){
}
public Actor(Integer id, String name, String sex, Date bornDate, String phone) {
this.id = id;
this.name = name;
this.sex = sex;
this.bornDate = bornDate;
this.phone = phone;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public void setBornDate(Date bornDate) {
this.bornDate = bornDate;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public Date getBornDate() {
return bornDate;
}
public String getPhone() {
return phone;
}
}
utils
private static DataSource ds;
//在靜態代碼塊完成ds初始化
static{
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
ds= DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
new RuntimeException(e);
}
}
//得到連接方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
//釋放資源,Connection放回連接池,此是的close是資料庫連接池實作的close方法,
public static void close(ResultSet rs, Connection conn, Statement st) throws SQLException {
if(rs!=null){
rs.close();
}
if (conn!=null){
conn.close();
}
if(st!=null){
st.close();
}
}
測驗使用
//測驗ActorDao對actor表crud操作
public void testActorDao() throws SQLException {
ActorDAO actorDAO = new ActorDAO();
//1.查詢
List<Actor> actors = actorDAO.queryMulti("select *from actor where id>?", Actor.class, 1);
//2.查詢單行
Actor actor01 = actorDAO.querySingle("select *from actor where id=?", Actor.class, 1);
//3.查詢單行單列
Object o = actorDAO.queryScalar("select name from actor where id=?", 6);
//4.dml操作
//添加資料是按照什么順序呢?
int afftedRows = actorDAO.update("insert into actor values(null,?,?)", "nihao", "niuma");
}
作者:程式員包子,轉載請注明原文鏈接:https://www.cnblogs.com/coder-baozi/p/16290759.html
coder-baozi一位菜鳥碼農
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/478315.html
標籤:.NET技术
上一篇:在Python中繪制圖形
