.超炫酷HTML5 Canvas 3D旋轉地球動畫
這是一款基于HTML5 Canvas的3D地球模擬動畫,動畫以太空作為背景,地球在太空中旋轉,同時我們也可以拖拽鼠標來從不同的角度觀察地球。另外我們也可以通過點擊全屏按鈕來全屏觀看地球旋轉動畫,記得在很早以前我們也分享過一款基于HTML5 Canvas的地球動畫,請看這里。
2.HTML5 Canvas 3D文字動畫 支持鼠標滾輪縮放
之前我們已經為大家分享過很多款炫酷和實用的HTML5文字動畫特效,最經典的就是這款HTML5 Canvas幻彩火焰文字特效。這次給大家帶來的是另外一款基于HTML5 Canvas 3D文字動畫,它的特點是按住鼠標左鍵拖動文字可以旋轉文字,從不同角度觀察文字;按住鼠標左鍵可以移動文字;另外滑動鼠標滾輪可以縮放文字大小。
3.HTML5 Canvas五彩煙霧模擬動畫
之前我們利用HTML5技術在Canvas上模擬了很多東西,比如最近剛分享的HTML5 Canvas 多種炫酷3D粒子圖形動畫。這次給大家帶來的也是一款基于HTML5 Canvas的動畫特效,它模擬了五彩煙霧的流動效果,這在Canvas上實現還是比較簡單的。
4.CSS3帶頭像的垂直彩色菜單
今天我們要給大家分享一款比較特別的CSS3菜單,這款菜單是垂直樣式的,而且當鼠標滑過菜單項時,當前菜單項即可顯示一個精美的頭像,并且在頭像旁邊顯示一些格式化的文字。另外,每一個菜單項的背景色可以動態渲染。
5.HTML5/CSS3 3D立方體拼圖 支持方向鍵旋轉
之前我們分享過不少基于HTML5/CSS3和Canvas的3D立方體旋轉動畫,比如這款超絢麗CSS3多色彩發光立方體旋轉動畫,也有基于立方體做的菜單應用,比如超實用CSS3 3D菜單 菜單項懸浮凸出立體效果。今天我們要分享的是一款基于HTML5和CSS3的3D立方體拼圖應用,一共有8個小立方體組成的3D拼圖,我們可以點擊立方體或者方向鍵完成拼圖,同時我們也可以讓立方體保持旋轉。
6.CSS3/SVG實現的任務列表 超酷的按鈕點擊動畫
這是一款外觀很漂亮的CSS3/SVG任務列表插件,我們可以在輸入框中輸入自己需要完成的任務名稱,點擊添加按鈕后即可將任務添加到列表中去。這本來是一件非常普通的功能,但是利用CSS3和SVG,我們在添加按鈕點擊時出現非常炫酷的動畫特效,這樣的特效在任務添加到列表和刪除列表中都同樣會出現,非常不錯。甚至我們可以將這款插件修改后變成一款非常華麗的評論插件。
7.CSS3水平滑桿插件 帶氣泡數值提示
今天我們要給大家介紹一款很酷的水平滑桿插件,和之前分享的jQuery雙向滑動桿 設置數值百分比和超可愛的純CSS3滑動開關按鈕類似,基本都是通過CSS3來美化瀏覽器默認的水平滑桿。今天分享的這款有一個特點,就是滑桿的數值帶有氣泡提示框,而且它就像氣球一樣拖動時會左右搖晃。
8.CSS3/SVG自定義單選框Radiobox跳躍選擇動畫
這又是一款利用CSS3實現的自定義美化版Radiobox單選框,和之前分享的CSS3自定義發光radiobox單選框類似,這款radiobox也是利用CSS3重寫radiobox樣式實現的。另外,這款自定義單選框還結合了SVG的特性,實現了單選框選中時的跳躍動畫。
9.基于HTML5 WebGL的3D星云動畫特效
今天我們給大家分享一個基于HTML5 Canvas的星云動畫特效,整個畫面模擬了一個星系的外觀,比如模擬了太陽系,有很多小行星圍繞著星系中心旋轉,星系中心也顯得格外亮麗。這些小星點都是在Canvas上繪制而成,同時我們還可以拖拽鼠標從不同視角觀看星云,非常大氣。
10.jQuery Select下拉框美化插件 菜單淡如淡出動畫
盡管現在的瀏覽器更新換代后更加強大,瀏覽器默認控件也逐漸變得美觀起來,特別是Select下拉框,已經不再是以前IE6那樣的丑陋了。但是今天我們要為大家分享一款基于jQuery的Select下拉框美化插件,它完全重寫了瀏覽器默認的Select下拉框樣式,而且在下拉菜單展開時還伴隨淡如淡出的動畫效果,非常不錯。當然我們以前也分享過一些類似的插件,可以看看漂亮實用的Select下拉框美化插件Tether,也可以看看這款jQuery 美化界面的下拉框。
本文固定鏈接: http://www.i7758.com/archives/2866.html
局一張圖
上圖是京東的登錄頁面,這和我們接下來要學習的東西有關系。
form表單就是專門用來實現用戶登錄、用戶注冊、信息收集之類的頁面的。
日常網購一般都要求我們先登錄,輸入用戶名和密碼,點擊登錄后才可以進行物品的購買,那我們就用form表單來實現一下登錄。
form表單中包含了input標簽,input標簽屬于單標簽。單標簽是相對于雙標簽來講的。以前的a標簽、p標簽、span標簽等,都屬于雙標簽,它們的特點是一對一對的,比如a標簽,要寫成<a></a>。比如p標簽,要寫成<p></p>,它們都是有一個開始,有一個結束。都是成雙入對的。
單標簽則沒有結束標簽,比如上面的input標簽,它就沒有</input>這樣的結束。單標簽還有我們之前學的img圖片標簽,br換行標簽等。
input標簽只能包含在form標簽中,也就是說只要有input出現的地方,必定會有一個form標簽包圍著它。
而上一篇中的tr和td標簽只能包含在table表格標簽中,它們是組合。不能分開單獨使用。
同樣的情況還有li標簽,只能包含在ul無序列表標簽,ol有序列表標簽中,dt標簽和dd標簽只能包含在dl標簽中。
上面代碼在瀏覽器中的效果:
這樣一個簡易的登錄頁面就做出來了,是不是很簡單啊,我們試著填寫用戶名和密碼來嘗試一下登錄。
你會發現,你輸入的密碼并不是明文的,也就是說你輸入的密碼會以黑點的形式呈現,這是為了安全,如果不這樣,你輸密碼的時候很可能會被你背后的人看到。
那這個是怎么實現的呢?其實你應該已經發現了,input標簽里面有個type屬性,type設置為password就是以密碼形式呈現。type設置為text就是以明文顯示,type設置為submit就是一個按鈕。
提交按鈕的input里面我們還設置了一個value屬性,這個屬性用來設置,按鈕的文字,我們這里設置的是登錄,為了加深理解,我們給它改為登入。
瀏覽器中就會相應地顯示為登入:
form表單中除了可以寫input外,還可以有select下拉選擇標簽、textarea文本域標簽、button按鈕標簽(這個按鈕和<input type="submit">都是按鈕,用的時候看你自己喜好)。
下面通過一個例子來了解上面的各種標簽的使用。
案例:添加一篇文章,要求填寫文章標題、文章分類、發表頻道、文章內容。
瀏覽器中的效果:
上述代碼中form標簽中有select標簽,這個的作用是產生一個下拉框,供用戶選擇。
select里面包含option標簽,這個就是具體可以選擇項。我們這里設置了三個:財經、教育、歷史。 同我們前面講的一樣,option必須包含在select里面,而select必須包含在form表單標簽中,它們是組合,不能打單獨使用。
在這個例子中,我們還是用到了單選。
紅框框住的就是我們設置的單選,具體對應代碼中的:
單選也是用的input標簽,只不過它的type是radio,還有一個要注意的地方就是name屬性,我們這里可以選擇的項是:新視覺、獵奇、雜說,它們三個的name屬性都設置為了:channel。只有設置name都一樣,才能單選,否則就成了多選了。
文章內容我們使用到了textarea文本域標簽,它和<iniput type="text">的區別是,textarea可以放更多的文字。
textarea標簽有兩個屬性需要說一下,cols代表的是列,rows代表的是行,cols設置的值越大,textarea的框越寬,rows的值設置的越大,textarea的框越高。我們來嘗試一下:
先把cols設置成60,textarea就會變寬。
rows設置為30,textarea就會變高 :
form表單中還可以設置多選,我們也來嘗試一下:
瀏覽器中的效果:
紅框框住的部分就是我們實現的多選,多選用的也是input標簽,type為checkbox
你可以自己動手嘗試一下
我們整體的需求都實現了,但是整個頁面看起來很不美觀,所以我們需要調整一下,我決定采用table表格來調整,把文章標題、請選擇分類、內容這些文字性的東西放到一列,input、select等標簽放在另一列里面,使整個頁面看起來更加整齊。
瀏覽器中的效果:
經過調整以后,看起來已經不那么丑了,在《做網站需要學習哪些知識》中,我們介紹過美化頁面需要用Css和JavaScript,光用html是不可能做到很美觀的。
在講完html后,我們就會講Css和JavaScript。
HTML 結構:瀏覽器頁面的結構(骨架)
CSS 表現:美化頁面,讓頁面更好看
JavaScript 行為:讓頁面動起來(比如表單驗證)
JavaScript的庫:jQuery(把JavaScript封裝起來,寫代碼更方便)
前端主流框架:Vue
Tomcat服務器
XML:可以自定義標簽,寫配置文件
三大組件:
Servlet:寫java代碼,與瀏覽器交互。
1.獲取用戶從瀏覽器發來的請求
2.處理請求
3.響應(回復)請求
Filter:過濾器(過濾數據)
Listener:監聽器(監聽一些響應的操作)
JSP:本質上是Servlet,幫助Servlet實現動態頁面,為客戶端回傳數據。
EL表達式:代替jsp中的<%= %>
JSTL標簽庫:代替jsp中的<% %>
幫助服務器判斷多次請求是否來自于同一個瀏覽器
(比如淘寶,在首頁登陸了,再別的頁面也應該是登錄狀態,不能換一個頁面還要重新登錄)
瀏覽器的Cookie:
服務器端Session:
Ajax:實現異步請求(多個請求同時進行)
用例1:注冊時判斷用戶名是否重復等
用例2:在百度搜索時,寫幾個關鍵詞,出現很多提示
服務器和瀏覽器之間傳輸數據時,可以通過xml,但是現在都是通過JSON(json更簡單便捷)
1.1 數據的持久化
1.2 Java中的數據存儲技術
JDO是對JDBC的封裝
1.3 JDBC介紹
JDBC是接口(一組規范)
JDBC驅動是JDBC接口的實現類的集合,有各大數據庫廠家完成。
1.4 JDBC體系結構
1.5 JDBC程序編寫步驟
2.4 數據庫連接方式舉例
== 首先將提供的數據庫添加到mysql中,如下是原文件 ==
文件名:shangguigu_jdbc_test.sql
DROP TABLE IF EXISTS `customers`;
CREATE TABLE IF NOT EXISTS `customers` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(15) DEFAULT NULL,
`email` varchar(20) DEFAULT NULL,
`birth` date DEFAULT NULL,
`photo` mediumblob,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `customers` (`id`, `name`, `email`, `birth`, `photo`) VALUES
(1, '汪峰', 'wf@126.com', '2010-02-02', NULL),
(2, '王菲', 'wangf@163.com', '1988-12-26', NULL),
(3, '林志玲', 'linzl@gmail.com', '1984-06-12', NULL),
(4, '湯唯', 'tangw@sina.com', '1986-06-13', NULL),
(5, '成龍', 'Jackey@gmai.com', '1955-07-14', NULL),
(6, '迪麗熱巴', 'reba@163.com', '1983-05-17', NULL),
(7, '劉亦菲', 'liuyifei@qq.com', '1991-11-14', NULL),
(8, '陳道明', 'bdf@126.com', '2014-01-17', NULL),
(10, '周杰倫', 'zhoujl@sina.com', '1979-11-15', NULL),
(12, '黎明', 'LiM@126.com', '1998-09-08', NULL),
(13, '張學友', 'zhangxy@126.com', '1998-12-21', NULL),
(16, '朱茵', 'zhuyin@126.com', '2014-01-16', NULL),
(18, '貝多芬', 'beidf@126.com', '2014-01-17', NULL);
DROP TABLE IF EXISTS `examstudent`;
CREATE TABLE IF NOT EXISTS `examstudent` (
`FlowID` int NOT NULL AUTO_INCREMENT,
`Type` int DEFAULT NULL,
`IDCard` varchar(18) DEFAULT NULL,
`ExamCard` varchar(15) DEFAULT NULL,
`StudentName` varchar(20) DEFAULT NULL,
`Location` varchar(20) DEFAULT NULL,
`Grade` int DEFAULT NULL,
PRIMARY KEY (`FlowID`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=gb2312;
INSERT INTO `examstudent` (`FlowID`, `Type`, `IDCard`, `ExamCard`, `StudentName`, `Location`, `Grade`) VALUES
(1, 4, '412824195263214584', '200523164754000', '張鋒', '鄭州', 85),
(2, 4, '222224195263214584', '200523164754001', '孫朋', '大連', 56),
(3, 6, '342824195263214584', '200523164754002', '劉明', '沈陽', 72),
(4, 6, '100824195263214584', '200523164754003', '趙虎', '哈爾濱', 95),
(5, 4, '454524195263214584', '200523164754004', '楊麗', '北京', 64),
(6, 4, '854524195263214584', '200523164754005', '王小紅', '太原', 60);
DROP TABLE IF EXISTS `order`;
CREATE TABLE IF NOT EXISTS `order` (
`order_id` int NOT NULL AUTO_INCREMENT,
`order_name` varchar(20) DEFAULT NULL,
`order_date` date DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `order` (`order_id`, `order_name`, `order_date`) VALUES
(1, 'AA', '2010-03-04'),
(2, 'BB', '2000-02-01'),
(4, 'GG', '1994-06-28');
DROP TABLE IF EXISTS `user`;
CREATE TABLE IF NOT EXISTS `user` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(10) NOT NULL,
`password` varchar(15) NOT NULL DEFAULT '123456',
`address` varchar(25) DEFAULT NULL,
`phone` varchar(15) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `user` (`id`, `name`, `password`, `address`, `phone`) VALUES
(1, '章子怡', 'qwerty', 'Beijing', '13788658672'),
(2, '郭富城', 'abc123', 'HongKong', '15678909898'),
(3, '林志玲', '654321', 'Taiwan', '18612124565'),
(4, '梁靜茹', '987654367', 'malaixiya', '18912340998'),
(5, 'LadyGaGa', '123456', 'America', '13012386565');
DROP TABLE IF EXISTS `user_table`;
CREATE TABLE IF NOT EXISTS `user_table` (
`user` varchar(20) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL,
`balance` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `user_table` (`user`, `password`, `balance`) VALUES
('AA', '123456', 1000),
('BB', '654321', 1000),
('CC', 'abcd', 2000),
('DD', 'abcder', 3000);
2.4.1 連接方式一
package com.atguigu.connection;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;
public class ConnectionTest {
@Test
//數據庫連接方式一
public void testConnection1() throws SQLException {
//1.通過Driver接口來獲取數據庫連接,Driver就是驅動的意思
/*
2.如果使用mysql:Driver driver = mysql具體的jdbc接口實現類(即mysql驅動)。
需要添加mysql驅動jar包,這里使用:mysql-connector-java-8.0.28.jar。
使用哪個實現類?
可以將鼠標放在下方Driver上,然后按ctrl+H 或者 ctrl+shift+B,
就可以看到Driver接口有哪些實現類。
因為我的mysql是高版本,選擇com.mysql.cj.jdbc.Driver()
注意:mysql高版本要有cj
*/
Driver driver = new com.mysql.cj.jdbc.Driver();
//url:統一資源定位符。
//用來標識被注冊的驅動。通過url選擇正確的驅動程序,從而建立到數據庫的正確連接。
//通俗說:就是連接到哪個數據庫
/*
jdbc:mysql 協議
localhost ip地址
3306 默認端口號
test 數據庫名(需要將數據庫添加到mysql中)
*/
String url = "jdbc:mysql://localhost:3306/shangguigu_jdbc_test";
/*
獲取mysql數據庫連接需要輸入自己設置的mysql的用戶名和密碼:
將用戶名和密碼封裝在Properties中。
*/
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","abc123");
//通過Driver的connect方法獲得Connection對象
//把鼠標放在connect上就可以看到提示信息,輸入什么參數。
Connection connection = driver.connect(url, info);
System.out.println(connection);
}
}
1.添加mysql驅動jar包,這里使用:mysql-connector-java-8.0.28.jar
在項目下創建一個lib目錄,將jar包添加進去,然后右擊jar包點擊add as library
2.4.1 連接方式二
@Test
//方式二:方式一的迭代(使程序中不出現第三方的api接口,使程序具有更好的可移植性)
public void testConnection2() throws Exception {
//1.獲取Driver接口的實現類對象:使用反射
//這樣使用不同的數據庫管理系統,只用修改括號里的內容就好了。
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
//其他和方式一相同
//2.提供要連接的數據庫
String url = "jdbc:mysql://localhost:3306/shangguigu_jdbc_test";
//3.提供連接需要的用戶名和密碼
Properties info = new Properties();
info.setProperty("user","root");
info.setProperty("password","abc123");
//4.獲取連接
Connection connection = driver.connect(url, info);
System.out.println(connection);
}
2.4.1 連接方式三
@Test
//方式三:使用DriverManager類(驅動管理器)替代Driver
public void testConnection3() throws Exception{
//1.獲取Driver接口的實現類對象
Class clazz = Class.forName("com.mysql.jdbc.Driver");
Driver driver = (Driver) clazz.newInstance();
//注冊驅動
DriverManager.registerDriver(driver);
//2.提供3個基本信息
String url = "jdbc:mysql://localhost:3306/shangguigu_jdbc_test";
String user = "root";
String password = "abc123";
//3.獲取連接
Connection connection = DriverManager.getConnection(url,user,password);
System.out.println(connection);
}
2.4.1 連接方式四
@Test
//方式四:優化方式三
public void testConnection4() throws Exception{
//1.獲取Driver接口的實現類對象
//只需要寫這一行代碼就可以了,因為Driver類中有一個靜態代碼塊,該靜態代碼塊已經將其他代碼寫好了。
//類加載時靜態代碼塊執行。
Class.forName("com.mysql.jdbc.Driver");
//注意: Class.forName("com.mysql.jdbc.Driver"); 不寫也行!
//在導入mysql-connector-java-8.0.28.jar包之后,反射獲取實現類對象和注冊驅動都幫你寫好了。
//但是在mysql下可以這么做,在其他數據庫管理系統下如Oracle,就不能這么做了。所以還是保留這行代碼。
//2.提供3個基本信息
String url = "jdbc:mysql://localhost:3306/shangguigu_jdbc_test";
String user = "root";
String password = "abc123";
//3.獲取連接
Connection connection = DriverManager.getConnection(url,user,password);
System.out.println(connection);
}
2.4.1 連接方式五(最終版)
@Test
//方式五(最終版):將數據庫連接需要的4個配置信息寫在配置文件中
//在src下創建一個jdbc.properties
public void testConnection5() throws Exception{
//1.讀取配置文件中的4個基本信息
//這里使用資源綁定器ResourceBundle
/*
第一步:使用ResourceBundle.getBundle(Properties文件名 不加擴展名.properties)獲得資源綁定器
也可以說是將資源綁定器綁定在Properties文件上。
第二步:通過getString(key)方法獲得文件中的信息。
注意:Properties文件必須在src下(類路徑下);該文件必須以.properties結尾。
*/
ResourceBundle resourceBundle = ResourceBundle.getBundle("jdbc");
String driver = resourceBundle.getString("driver");
String user = resourceBundle.getString("user");
String password = resourceBundle.getString("password");
String url = resourceBundle.getString("url");
//2.加載驅動
Class.forName(driver);
//3.獲取連接
Connection connection = DriverManager.getConnection(url,user,password);
System.out.println(connection);
}
jdbc.properties:
#順序隨便,變量命名隨便,等號左右不要有空格
driver=com.mysql.cj.jdbc.Driver
user=root
password=abc123
url=jdbc:mysql://localhost:3306/shangguigu_jdbc_test
3.1 操作和訪問數據庫
3.2 使用Statement操作數據表的弊端
3.3.1 PreparedStatement介紹
3.3.2 使用PreparedStatement實現 增insert 操作
package com.atguigu2.preparedStatement;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ResourceBundle;
//使用PreparedStatement代替Statement實現對數據表的增刪改查操作
//增刪改和查
public class PreparedStatementCRUDTest {
@Test
//增操作:向customers表中添加一條數據。
public void testInsert(){
Connection connection = null;
PreparedStatement ps = null;
try{
//1.獲取數據庫連接
ResourceBundle resourceBundle = ResourceBundle.getBundle("jdbc");
String driver = resourceBundle.getString("driver");
String user = resourceBundle.getString("user");
String password = resourceBundle.getString("password");
String url = resourceBundle.getString("url");
Class.forName(driver);
connection = DriverManager.getConnection(url,user,password);
//2.預編譯sql語句
//?:占位符。只有這樣才能解決sql注入問題。
String sql = "insert into customers(name,email,birth) values(?,?,?)";
//3.獲取PreparedStatement對象(通過Connection的prepareStatement(sql語句)方法)
ps = connection.prepareStatement(sql);
/*
4.填充占位符(通過PrepareStatement的setXXX()方法)
如果name是在sql中是字符串類型,則setString(),其他的類似。
ps.setString(int parameterIndex,String x);
parameterIndex為參數下標,從1開始,1代表第一個占位符;2代表第二個占位符。
*/
ps.setString(1,"哪吒");
ps.setString(2,"nezha@gmail.com");
//birth在sql中是date類型,解釋看下面
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date utilDate = sdf.parse("2000-01-01");
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
ps.setDate(3,sqlDate);
//5.執行insert操作
ps.execute();
}catch (Exception e){
e.printStackTrace();
}finally {
//6.資源的關閉
try {
//關閉之前避免出現空指針
if (ps!=null){
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
//關閉之前避免出現空指針
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
關于如下幾行代碼的解釋:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date utilDate = sdf.parse("2000-01-01");
System.out.println(utilDate); //Sat Jan 01 00:00:00 CST 2000
System.out.println(utilDate.getTime()); //946656000000
java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
System.out.println(sqlDate); //2000-01-01
ps.setDate(3,sqlDate);
1.birth在sql中是date類型,所以ps.setDate(3,這個參數要是Date類型)
2.java中的Date和sql中的Date不同:
java中的Date:java.util.Date
sql中的Date:java.sql.Date
大致區別就是java.util.Date支持日期和時間,而java.sql.Date只支持日期
所以要進行轉換。
3. getTime()方法獲得從1970年1月1日到指定時間之間的毫秒數
3.3.3 JDBCUtils:封裝數據庫連接和關閉操作
從代碼中可以看出,數據庫的連接和關閉操作代碼不變且一直重復,所以考慮封裝。
package com.atguigu3.util;
import java.sql.*;
import java.util.ResourceBundle;
//封裝數據庫連接和關閉操作的工具類
public class JDBCUtils {
//工具類中一般采用靜態方法
//獲取數據庫連接
public static Connection getConnection() throws Exception{
ResourceBundle resourceBundle = ResourceBundle.getBundle("jdbc");
String driver = resourceBundle.getString("driver");
String user = resourceBundle.getString("user");
String password = resourceBundle.getString("password");
String url = resourceBundle.getString("url");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url,user,password);
return connection;
}
//關閉資源
//參數本來應該寫PreparedStatement,但是PreparedStatement是Statement是子接口,
//所以寫Statement范圍更大點,既可以傳PreparedStatement,也可以傳Statement。
public static void closeResource(Connection connection, Statement ps){
try {
//關閉之前避免出現空指針
if (ps!=null){
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
//關閉之前避免出現空指針
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3.3.4 通用的 增刪改 操作
同一個數據庫中的不同表
/*
通用的 增刪改 操作:
增刪改操作基本上是一樣的,不同在于:預編譯sql語句和填充占位符。
因為不知道有幾個占位符,需要填充幾個占位符。這里使用可變長度形參來表示要填充的占位符。
sql中的占位符個數=可變形參長度
*/
public void update(String sql,Object ...agrs){
Connection connection = null;
PreparedStatement ps = null;
try{
//1.獲取數據庫連接
connection = JDBCUtils.getConnection();
//2.預編譯sql語句(由形參傳入),返回PreparedStatement對象實例
ps = connection.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < agrs.length; i++) {
ps.setObject(i+1,agrs[i]);
}
//4.執行
ps.execute();
}catch (Exception e){
e.printStackTrace();
}finally {
//5.釋放資源
JDBCUtils.closeResource(connection,ps);
}
}
@Test
//測試通用的 增刪改 操作
public void testCommonUpdate(){
//刪除delete操作
String sql1 = "delete from customers where id = ?";
update(sql1,3);
//改update操作
//注意:在mysql中,表名不能和關鍵字一樣,如下面的order表就和關鍵字order一樣了,這樣會報錯。
//解決辦法:加著重符`order`,就可以解決沖突;或者通過數據庫名.表名的方式來寫。
String sql2 ="update `order` set order_name = ? where order_id = ?";
update(sql2,"DD","2");
}
3.3.5 Java與SQL對應數據類型轉換表
3.3.6.1 針對customers表的 不通用的 查select 操作
package com.atguigu2.preparedStatement;
import com.atguigu3.bean.Customer;
import com.atguigu3.util.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
//使用PreparedStatement實現 查select操作
public class PreparedStatementCustomerForQuery {
@Test
public void testQuery1(){
Connection connection = null;
PreparedStatement ps =null;
ResultSet resultSet = null;
try{
//1.獲取數據庫連接
connection = JDBCUtils.getConnection();
//2.預編譯sql語句,返回PreparedStatement對象實例
String sql = "select id,name,email,birth from customers where id = ?";
ps = connection.prepareStatement(sql);
//3.填充占位符
ps.setObject(1,1);
//4.執行,并返回結果集
resultSet = ps.executeQuery();
//5.處理結果集
//next():判斷結果集的下一條是否有數據,如果有數據返回true,沒有數據返回false。
//這里演示獲取一條數據,使用if
if (resultSet.next()){
//獲取當前這條數據的各個字段值(查看Java與SQL對應數據類型轉換表)
int id = resultSet.getInt(1);
String name = resultSet.getString(2);
String email = resultSet.getString(3);
Date birth = resultSet.getDate(4);
//輸出方法
//方式一:
//System.out.println("id = "+id+",name = "+name+",email = "+email+",birth = "+birth);
//方式二:數組形式
//Object[] data = new Object[]{id,name,email,birth};
//方式三:創建一個Customer類,將數據封裝為一個對象(推薦)
Customer customer = new Customer(id,name,email,birth);
System.out.println(customer);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//關閉資源
//此時多了一個ResultSet,所以回到JDBCUtils重載closeResource()方法。
JDBCUtils.closeResource(connection,ps,resultSet);
}
}
}
package com.atguigu3.bean;
import java.util.Date;
/*
ORM編程思想(object relational mapping)
一個表對應一個java類
表中的一條數據對應java類的一個對象
表中的一個字段對應java類的一個屬性
*/
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer() {
}
public Customer(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth=" + birth +
'}';
}
}
package com.atguigu3.util;
import java.sql.*;
import java.util.ResourceBundle;
//封裝數據庫連接和關閉操作的工具類
public class JDBCUtils {
//工具類中一般采用靜態方法
//獲取數據庫連接
public static Connection getConnection() throws Exception{
ResourceBundle resourceBundle = ResourceBundle.getBundle("jdbc");
String driver = resourceBundle.getString("driver");
String user = resourceBundle.getString("user");
String password = resourceBundle.getString("password");
String url = resourceBundle.getString("url");
Class.forName(driver);
Connection connection = DriverManager.getConnection(url,user,password);
return connection;
}
//關閉資源
//參數本來應該寫PreparedStatement,但是PreparedStatement是Statement是子接口,
//所以寫Statement范圍更大點,既可以傳PreparedStatement,也可以傳Statement。
public static void closeResource(Connection connection, Statement ps){
try {
//關閉之前避免出現空指針
if (ps!=null){
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
//關閉之前避免出現空指針
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void closeResource(Connection connection,PreparedStatement ps,ResultSet resultSet){
try {
//關閉之前避免出現空指針
if (resultSet!=null){
resultSet.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
//關閉之前避免出現空指針
if (ps!=null){
ps.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
//關閉之前避免出現空指針
if (connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//針對customers表的 通用的 查select 操作
public Customer queryForCustomers(String sql,Object ...args) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
//1.獲取數據庫連接
conn = JDBCUtils.getConnection();
//2.預編譯sql語句(有參數傳遞),返回PreparedStatement對象實例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
//4.執行,并返回結果集
rs = ps.executeQuery();
//5.處理結果集(獲取當前這條數據的各個字段值,并將其封裝為一個Customer對象)
//Customer類中的屬性名和表中的列名要一致
//獲取結果集的元數據
ResultSetMetaData rsmd = rs.getMetaData();
//通過元數據獲取結果集中的列數
int columnCount = rsmd.getColumnCount();
//這里演示獲取一條數據,使用if
if (rs.next()){
//采用空構造方法,然后set方法;而不是含參構造方法,因為不知道傳遞過來幾個參數
Customer customer = new Customer();
//接下來需要知道結果集中每一列的列名和列值才能設置一個Customer對象
//有幾列?通過元數據ResultSetMetaData 獲取結果集中的列數
for (int i = 0; i < columnCount; i++) {
//獲取列值
Object columnValue = rs.getObject(i+1);
//獲取每個列的列名
//String columnName = rsmd.getColumnName(i+1);
//由 針對order表的 通用的 查select 操作 (3.3.8) 知道:使用getColumnLabel()方法
String columnLabel = rsmd.getColumnLabel(i+1);
//為Customer對象的columnLabel屬性 賦值為 columnValue (即Customer類中的set方法):通過反射
Field field = Customer.class.getDeclaredField(columnLabel);//得到Customer類中的某個屬性對象
field.setAccessible(true);//可以對該屬性對象的set方法進行操作
field.set(customer,columnValue);//使用該屬性的set方法
}
return customer;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6.關閉資源
JDBCUtils.closeResource(conn,ps,rs);
}
//結果集中什么都沒有,返回null
return null;
}
@Test
//測試:針對customers表的 通用的 查select 操作
public void testQueryForCustomers(){
String sql = "select id,name,birth,email from customers where id = ?";
Customer customer = queryForCustomers(sql,13);
System.out.println(customer);
String sql1 = "select name,email from customers where name = ?";
Customer customer1 = queryForCustomers(sql1,"周杰倫");
System.out.println(customer1);
}
如果是select *
String sql = "select * from customers"; 這樣是錯的。
解決辦法:String sql = "select 所有的表中的字段名 from customers";
即 String sql = "select id,name,email,birth from customers";
3.3.6.3 針對order表的 通用的 查select 操作
這里會出現一個問題:
使用通用操作時,若表的字段名和Order類的屬性名不一致,則按照上述方法會出錯。
解決方法:為列的列名起別名(別名按照Order類的屬性名來起)
package com.atguigu2.preparedStatement;
import com.atguigu3.bean.Customer;
import com.atguigu3.bean.Order;
import com.atguigu3.util.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.io.ObjectStreamException;
import java.lang.reflect.Field;
import java.sql.*;
public class PreparedStatementOrderForQuery {
@Test
//針對order表的 查select操作(不通用)
public void testQuery1() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
//1.獲取數據庫連接
conn = JDBCUtils.getConnection();
//2.預編譯sql語句和返回PreparedStatement對象實例
String sql = "select order_id,order_name,order_date from `order` where order_id = ?";
ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setObject(1,1);
//4.執行,并返回結果集
rs = ps.executeQuery();
//5.處理結果集
//這里演示獲取一條數據,使用if
if (rs.next()){
int id = rs.getInt(1);
String name = rs.getString(2);
Date date = rs.getDate(3);
Order order = new Order(id,name,date);
System.out.println(order);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6.關閉資源
JDBCUtils.closeResource(conn,ps,rs);
}
}
/*
當表的字段名和類的屬性名不同時:
1.在聲明sql時,使用類的屬性名來命名字段的別名
2.使用ResultSetMetaData的getColumnLabel()方法來替代getColumnName()方法,獲得列的列名的別名。
3.說明:沒有別名時,getColumnLabel()方法獲取的就是列名;所以無論有沒有別名,都使用getColumnLabel()方法
*/
//針對order表的 通用的 查select 操作
public Order orderForQuery(String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
//1.獲取數據庫連接
conn = JDBCUtils.getConnection();
//2.預編譯sql語句(有參數傳遞),返回PreparedStatement對象實例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
//4.執行,并返回結果集
rs = ps.executeQuery();
//5.處理結果集
//獲取結果集的元數據
ResultSetMetaData rsmd = rs.getMetaData();
//獲取列數
int columnCount = rsmd.getColumnCount();
//這里演示獲取一條數據,使用if
if (rs.next()){
Order order = new Order();
for (int i = 0; i < columnCount; i++) {
//獲取每個列的列值:通過ResultSet
Object columnValue = rs.getObject(i+1);
//獲取每個列的列名:通過ResultSetMetaData
//String columnName = rsmd.getColumnName(i+1);
//獲取每個列的列名的別名:通過ResultSetMetaData的getColumnLabel()方法
String columnLabel = rsmd.getColumnLabel(i+1);
//通過反射:為Order對象的columnLabel屬性 賦值為 columnValue (即Order類中的set方法)
Field field = Order.class.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(order,columnValue);
}
return order;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6.關閉資源
JDBCUtils.closeResource(conn,ps,rs);
}
//結果集中什么都沒有,返回null
return null;
}
@Test
//測試:針對order表的 通用的 查select 操作
public void testQueryForCustomers(){
String sql = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?";
Order order = orderForQuery(sql,1);
System.out.println(order);
}
}
package com.atguigu3.bean;
import java.sql.Date;
public class Order {
private int orderId;
private String orderName;
private Date orderDate;
public Order() {
}
public Order(int orderId, String orderName, Date orderDate) {
this.orderId = orderId;
this.orderName = orderName;
this.orderDate = orderDate;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public Date getOrderDate() {
return orderDate;
}
public void setOrderDate(Date orderDate) {
this.orderDate = orderDate;
}
@Override
public String toString() {
return "Order{" +
"orderId=" + orderId +
", orderName='" + orderName + '\'' +
", orderDate=" + orderDate +
'}';
}
}
3.3.6.4 圖解查操作
3.3.6.5.1 查找返回一條數據
package com.atguigu2.preparedStatement;
import com.atguigu3.bean.Customer;
import com.atguigu3.bean.Order;
import com.atguigu3.util.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
//針對 不同的表 通用的 查 操作
public class PreparedStatementQueryTest {
/*
String sql:預編譯的sql語句
Object ...args:(可變形參)填充占位符
該方法返回一個類對象(查詢結果集表對應一個類,該表的每一條數據對應一個類對象)
每個表對應不同的類,所以使用泛型。
*/
public <T> T getInstance(Class<T> clazz,String sql, Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
//1.獲取數據庫連接
conn = JDBCUtils.getConnection();
//2.預編譯sql語句(有參數傳遞),返回PreparedStatement對象實例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
//4.執行,并返回結果集
rs = ps.executeQuery();
//5.處理結果集
//獲取結果集的元數據
ResultSetMetaData rsmd = rs.getMetaData();
//獲取列數
int columnCount = rsmd.getColumnCount();
//這里演示獲取一條數據,使用if
if (rs.next()){
T t = clazz.newInstance();
for (int i = 0; i < columnCount; i++) {
//獲取每個列的列值:通過ResultSet
Object columnValue = rs.getObject(i+1);
//獲取每個列的列名的別名:通過ResultSetMetaData的getColumnLabel()方法
String columnLabel = rsmd.getColumnLabel(i+1);
//通過反射:為clazz對象的columnLabel屬性 賦值為 columnValue (即clazz類中的set方法)
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
return t;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6.關閉資源
JDBCUtils.closeResource(conn,ps,rs);
}
//結果集中什么都沒有,返回null
return null;
}
@Test
//測試
public void testGetInstance(){
String sql = "select id,name,email from customers where id = ?";
Customer customer = getInstance(Customer.class,sql,12);
System.out.println(customer);
String sql1 = "select order_id orderId,order_name orderName,order_date orderDate from `order` where order_id = ?";
Order order = getInstance(Order.class,sql1,1);
System.out.println(order);
}
}
泛型
3.3.6.5.2 查找返回多條數據
//查找返回表中多條記錄
//返回一個集合對象
public <T> List<T> getForList(Class<T> clazz,String sql,Object ...args){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
//1.獲取數據庫連接
conn = JDBCUtils.getConnection();
//2.預編譯sql語句(有參數傳遞),返回PreparedStatement對象實例
ps = conn.prepareStatement(sql);
//3.填充占位符
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1,args[i]);
}
//4.執行,并返回結果集
rs = ps.executeQuery();
//5.處理結果集
//獲取結果集的元數據
ResultSetMetaData rsmd = rs.getMetaData();
//獲取列數
int columnCount = rsmd.getColumnCount();
//創建集合對象
List<T> list = new ArrayList<T>();
while (rs.next()){
T t = clazz.newInstance();
//給t對象指定的屬性賦值
for (int i = 0; i < columnCount; i++) {
//獲取每個列的列值:通過ResultSet
Object columnValue = rs.getObject(i+1);
//獲取每個列的列名的別名:通過ResultSetMetaData的getColumnLabel()方法
String columnLabel = rsmd.getColumnLabel(i+1);
//通過反射:為t對象的columnLabel屬性 賦值為 columnValue (即T類中的set方法)
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t,columnValue);
}
list.add(t);
}
//這里return list;寫在while外面
return list;
}catch (Exception e){
e.printStackTrace();
}finally {
//6.關閉資源
JDBCUtils.closeResource(conn,ps,rs);
}
//結果集中什么都沒有,返回null
return null;
}
@Test
//測試
public void testGetList(){
String sql = "select id,name,email from customers where id < ?";
List<Customer> list = getForList(Customer.class,sql,12);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//不寫占位符,這樣就輸出全部
String sql1 = "select id,name,email from customers";
List<Customer> list1 = getForList(Customer.class,sql1);
for (int i = 0; i < list1.size(); i++) {
System.out.println(list1.get(i));
}
}
使用PreparedStatement代替Statement原因:
1.PreparedStatement是預編譯的sql語句,可以解決Statement的拼串和sql注入問題;
PreparedStatement首先確定了語法邏輯,然后填充相應的數值;
而Statement會連著數值里包含的非法語法一起編譯,就會造成對原來語法邏輯的破壞。
2.PreparedStatement還可以操作Blob類型的數據,而Statement不行;
3.PreparedStatement可以實現跟高效的批量操作:
如果訪問10000條數據,PreparedStatement會將語法固定,只用填充占位符就好了。
1.獲取數據庫連接:采用獲取數據庫連接方式五(包括配置文件jdbc.properties)
2.操作數據庫:使用PreparedStatemnet操作數據庫
(注意要關閉資源,先開后閉原則)(異常使用try…catch…)
1)通用的 增刪改 操作:3.3.4 (包括JDBCUtils工具類 封裝數據庫的連接和資源的關閉)
2)通用的 查 操作: (包括JDBCUtils工具類 和 表對應的java類)
3.3.6.5.1 返回表中的一條數據
3.3.6.5.2 返回表中的多條數據
章節練習
使用PreparedStatement操作Blob類型的數據,Statement無法操作Blob類型的數據。
package com.atguigu4.blob;
import com.atguigu3.bean.Customer;
import com.atguigu3.util.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.sql.*;
//使用PreparedStatement操作Blob類型的數據
public class BlobTest {
@Test
//向數據表customers中插入Blob類型的數據
//customers表中的photo屬性就是Blob類型
public void testInsert() throws Exception {
//1.獲取數據庫連接
Connection conn = JDBCUtils.getConnection();
//2.預編譯sql語句,返回PreparedStatement對象實例
String sql = "insert into customers(name,email,birth,photo) values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setObject(1,"張三");
ps.setObject(2,"zhang@qq.com");
ps.setObject(3,"2000-09-08");
//photo的大小過大,要以文件的方式傳入
FileInputStream is = new FileInputStream("img/sea.png");
ps.setBlob(4,is);
//4.執行
ps.execute();
//5.關閉資源
JDBCUtils.closeResource(conn,ps);
}
//刪除Blob類型的數據,其實就是把整條數據刪除掉
//修改Blob類型的數據,跟上面的插入操作差不多,修改一下sql語句就好了
@Test
//查詢customers數據表中Blob類型的數據
public void testQuery() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
InputStream is = null;
FileOutputStream fos = null;
try{
//1.獲取數據庫連接
conn = JDBCUtils.getConnection();
//2.預編譯sql語句,返回PreparedStatement對象實例
String sql = "select id,name,email,birth,photo from customers where id = ?";
ps = conn.prepareStatement(sql);
//3.填充占位符
ps.setObject(1,25);
//4.執行,并返回結果集
rs = ps.executeQuery();
//5.處理結果集
if (rs.next()){
/*
方法一:
int id = rs.getInt(1);
String name = rs.getString(2);
String email = rs.getString(3);
Date birth = rs.getDate(4);
*/
//方法二:
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
Date birth = rs.getDate("birth");
Customer customer = new Customer(id,name,email,birth);
System.out.println(customer);
//獲取Blob類型的photo數據,以文件形式保存在本地
Blob photo = rs.getBlob("photo");
is = photo.getBinaryStream();
fos = new FileOutputStream("img/sea1.png");
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer))!=-1){
fos.write(buffer,0,len);
}
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6.關閉資源
try{
if (is!=null){
is.close();
}
}catch (Exception e){
e.printStackTrace();
}
try{
if (fos!=null){
fos.close();
}
}catch (Exception e){
e.printStackTrace();
}
JDBCUtils.closeResource(conn,ps,rs);
}
}
}
注意
使用PreparedStatement實現批量數據的操作
5.1 批量插入
package com.atguigu4.batchOperation;
import com.atguigu3.util.JDBCUtils;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
/*
使用PreparedStatement實現批量數據的操作
update和delete本身就具有批量操作的能力
update沒有過濾條件,將更改表中所有數據
delete沒有過濾條件,將刪除表中所有的數據
但是insert沒有這種能力
所以考慮:批量插入。
如何使用PreparedStatement實現高效的批量插入操作?
題目:創建一個goods表,向表中插入20000條數據
create table goods(
id int primay key auto_increment,
name varchar(25)
);
方式一:Statement(一般不使用這個)
Connection conn = JDBCUtils.getConnection();
Statement st = conn.createStatement();
for(int i=1;i<=20000;i++){
String sql = "insert into goods(name) values('name_"+ i +"')";
st.execute(sql);
}
*/
public class InsertTest {
@Test
//方式二:使用PreparedStatement
public void testInsert1(){
Connection conn = null;
PreparedStatement ps = null;
try{
conn = JDBCUtils.getConnection();
String sql = "insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
for (int i = 1; i <= 20000; i++) {
ps.setObject(1,"name_"+ i);
ps.execute();
}
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn,ps);
}
}
/*
方式二 相較于 方式一 優點:
即PreparedStatement優于Statement的地方:
在于sql語句
方式一內存中會有很多個sql語句,并且每次都會做一次語法檢查
而方式二只有一個sql語句,每次只是填充占位符。
*/
/*
方式三:對方式二的優化,更快一點。
1.使用Batch批量處理:addBatch()、executeBatch()、clearBatch()
2.mysql默認是關閉批處理的,我們需要一個參數打開批處理。
高版本mysql只需要在jdbc.properties配置文件的url后添上:
?rewriteBatchedStatements=true
方式二花費:27602毫秒
方式三花費:782毫秒
*/
@Test
public void testInsert2(){
Connection conn = null;
PreparedStatement ps = null;
try{
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
String sql = "insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
for (int i = 1; i <= 20000; i++) {
ps.setObject(1,"name_"+ i);
//1."攢"sql
ps.addBatch();
if (i%500 == 0){
//2.執行batch(有500條sql語句就執行一次)
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
/*
如果是插入19999條數據,不是整數怎么辦?
沒關系,只要添加下面這個代碼:
if(i==19999){
ps.executeBatch();
}
就好了,最好執行一次就好了。
*/
}
long end = System.currentTimeMillis();
System.out.println("花費的時間為:" + (end-start));
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn,ps);
}
}
/*
方式四(最終方案):再快點
每500條數據執行一次ps.executeBatch();
這樣就會提交一次。
每次提交都會占用一點時間,所以先不提交,都傳完以后,最后再提交。
*/
@Test
public void testInsert3(){
Connection conn = null;
PreparedStatement ps = null;
try{
long start = System.currentTimeMillis();
conn = JDBCUtils.getConnection();
//設置不允許自動提交數據
conn.setAutoCommit(false);
String sql = "insert into goods(name) values(?)";
ps = conn.prepareStatement(sql);
for (int i = 1; i <= 20000; i++) {
ps.setObject(1,"name_"+ i);
//1."攢"sql
ps.addBatch();
if (i%500 == 0){
//2.執行batch(有500條sql語句就執行一次)
ps.executeBatch();
//3.清空batch
ps.clearBatch();
}
}
//提交數據
conn.commit();
long end = System.currentTimeMillis();
System.out.println("花費的時間為:" + (end-start));
}catch (Exception e){
e.printStackTrace();
}finally {
JDBCUtils.closeResource(conn,ps);
}
}
}
PreparedStatement vs Statement
*請認真填寫需求信息,我們會在24小時內與您取得聯系。