1.JDBC(Java DataBase Connectivity)就是Java數(shù)據(jù)庫連接,說白了就是用Java語言來操作數(shù)據(jù)庫。
2.JDBC原理
最終得出的結(jié)論是,由SUN提供一套訪問數(shù)據(jù)庫的規(guī)范(就是一組接口),并提供連接數(shù)據(jù)庫的協(xié)議標(biāo)準(zhǔn),然后各個數(shù)據(jù)庫廠商會遵循SUN的規(guī)范提供一套訪問自己公司的數(shù)據(jù)庫服務(wù)器的API出現(xiàn)。SUN提供的規(guī)范命名為JDBC,而各個廠商提供的,遵循了JDBC規(guī)范的,可以訪問自己數(shù)據(jù)庫的API被稱之為驅(qū)動!
3.JDBC核心類(接口)介紹
JDBC中的核心類有:DriverManager、Connection、Statement,和ResultSet
DriverManager(驅(qū)動管理器)的作用有兩個:
注冊驅(qū)動:這可以讓JDBC知道要使用的是哪個驅(qū)動;
獲取Connection:如果可以獲取到Connection,那么說明已經(jīng)與數(shù)據(jù)庫連接上了。
Connection對象表示連接,與數(shù)據(jù)庫的通訊都是通過這個對象展開的:
Connection最為重要的一個方法就是用來獲取Statement對象;
Statement是用來向數(shù)據(jù)庫發(fā)送SQL語句的
voidexecuteUpdate(String sql):執(zhí)行更新操作(insert、update、delete等);
ResultSetexecuteQuery(String sql):執(zhí)行查詢操作,數(shù)據(jù)庫在執(zhí)行查詢后會把查詢結(jié)果,查詢結(jié)果就是ResultSet;
ResultSet對象表示查詢結(jié)果集,只有在執(zhí)行查詢操作后才會有結(jié)果集的產(chǎn)生
booleannext():使“行光標(biāo)”移動到下一行,并返回移動后的行是否存在
XXXgetXXX(int col):眾多get方法,獲取當(dāng)前行指定列上的值,參數(shù)就是列數(shù),列數(shù)從1開始,而不是0。
A:導(dǎo)jar包:驅(qū)動!
B:加載驅(qū)動類:Class.forName(“類名”);
C:給出ur、username、password,其中url背下來!
D:使用DriverManager類來得到Connection對象!
4.1:mysql數(shù)據(jù)庫的驅(qū)動jar包:mysql-connector-java-5.1.13-bin.jar
4.2:獲取連接
獲取連接需要兩步:
一是使用DriverManager來注冊驅(qū)動
:注冊驅(qū)動:
就只有一句話:Class.forName(“com.mysql.jdbc.Driver”)
原理:源碼中靜態(tài)代碼塊中會把com.mysql.jdbc.Driver注冊到DriverManager
DriverManager.registerDriver(newcom.mysql.jdbc.Driver());
雖然可以注冊驅(qū)動,屬硬編碼,依賴jar包,換數(shù)據(jù)庫需要改代碼
二是使用DriverManager來獲取Connection對象。
:獲取連接:
也只有一句代碼:DriverManager.getConnection(url,username,password)
username和password為數(shù)據(jù)庫用戶名和密碼
url:用來找到要連接數(shù)據(jù)庫“網(wǎng)址”
mysql的url:jdbc:mysql://localhost:3306/mydb1
例:
Connectioncon =DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydb1”,”root”,”123”);
4.3:獲取Statement
Statementstmt = con.createStatement();
Statement是用來向數(shù)據(jù)庫發(fā)送要執(zhí)行的SQL語句的!
4.4 發(fā)送SQL增、刪、改語句
Stringsql = “insert into user value(’zhangSan’, ’123’)”;
執(zhí)行更新操作,即執(zhí)行insert、update、delete,create table、alter table,drop table
intm = stmt.executeUpdate(sql);//m為影響的行數(shù)
4.5 發(fā)送SQL查詢語句
Stringsql = “select * from user”;
ResultSetrs = stmt.executeQuery(sql);
executeQuery()方法返回的是ResultSet,ResultSet封裝了查詢結(jié)果,稱之為結(jié)果集。
4.6 讀取結(jié)果集中的數(shù)據(jù)
rs.next();//光標(biāo)移動到第一行
rs.getInt(1);//獲取第一行第一列的數(shù)據(jù)
常用方法:
ObjectgetObject(int col)
StringgetString(int col)
intgetInt(int col)
doublegetDouble(int col)
滾動結(jié)果集光標(biāo)方法
voidbeforeFirst():把光標(biāo)放到第一行的前面,這也是光標(biāo)默認(rèn)的位置;
voidafterLast():把光標(biāo)放到最后一行的后面;
booleanfirst():把光標(biāo)放到第一行的位置上,返回值表示調(diào)控光標(biāo)是否成功;
booleanlast():把光標(biāo)放到最后一行的位置上;
booleanisBeforeFirst():當(dāng)前光標(biāo)位置是否在第一行前面;
booleanisAfterLast():當(dāng)前光標(biāo)位置是否在最后一行的后面;
booleanisFirst():當(dāng)前光標(biāo)位置是否在第一行上;
booleanisLast():當(dāng)前光標(biāo)位置是否在最后一行上;
booleanprevious():把光標(biāo)向上挪一行;
booleannext():把光標(biāo)向下挪一行;
booleanrelative(int row):相對位移,當(dāng)row為正數(shù)時,表示向下移動row行,為負(fù)數(shù)時表示向上移動row行;
booleanabsolute(int row):絕對位移,把光標(biāo)移動到指定的行上;
intgetRow():返回當(dāng)前光標(biāo)所有行。
獲取結(jié)果集元數(shù)據(jù)
得到元數(shù)據(jù):rs.getMetaData(),
返回值為ResultSetMetaData;
獲取結(jié)果集列數(shù):int getColumnCount()
獲取指定列的列名:String getColumnName(intcolIndex)
4.7 關(guān)閉資源
與IO流一樣,關(guān)閉的順序是先得到的后關(guān)閉,后得到的先關(guān)閉。
rs.close();
stmt.close();
con.close();
4.8 代碼
public class Demo2 { /* * 連接數(shù)據(jù)庫,得到Connection就算成功! * 對數(shù)據(jù)庫做增、刪、改 */ @Test public void fun1() throws ClassNotFoundException, SQLException { /* * 一、得到Connection * 1. 準(zhǔn)備四大參數(shù) * 2. 加載驅(qū)動類 * 3. 得到Connection */ // 準(zhǔn)備四大參數(shù) String driverClassName = "com.mysql.jdbc.Driver"; // jdbc協(xié)議的格式!jdbc:工商的名稱:子協(xié)議(由工商自己來規(guī)定) // 對mysql而言,它的子協(xié)議結(jié)構(gòu)://主機(jī):端口號/數(shù)據(jù)庫名稱 String url = "jdbc:mysql://localhost:3306/mydb3"; String username = "root"; String password = "123"; // 加載驅(qū)動類 Class.forName(driverClassName); // 使用DriverManager,以及省下的3個參數(shù),得到Connection Connection con = DriverManager.getConnection(url, username, password); /* * 二、對數(shù)據(jù)庫做增、刪、改 * 1. 通過Connection對象創(chuàng)建Statement * > Statement語句的發(fā)送器,它的功能就是向數(shù)據(jù)庫發(fā)送sql語句! * 2. 調(diào)用它的int executeUpdate(String sql),它可以發(fā)送DML、DDL */ // 1. 通過Connection得到Statement對象 Statement stmt = con.createStatement(); // 2. 使用Statement發(fā)送sql語句! // String sql = "INSERT INTO stu VALUES('ITCAST_0003', 'wangWu', 88, 'male')"; // String sql = "UPDATE stu SET name='zhaoLiu', age=22, " + // "gender='female' WHERE number='ITCAST_0003'"; String sql = "DELETE FROM stu"; int r = stmt.executeUpdate(sql); System.out.println(r); } /** * 執(zhí)行查詢 * @throws ClassNotFoundException * @throws SQLException */ @Test public void fun2() throws ClassNotFoundException, SQLException { /* * 一、得到Connection * 二、得到Statement,發(fā)送select語句 * 三、對查詢返回的“表格”進(jìn)行解析! */ /* * 一、得到連接 * 1. 準(zhǔn)備四大連接參數(shù) */ String driverClassName = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://localhost:3306/exam"; String username = "root"; String password = "123"; /* * 2. 加載驅(qū)動類 */ Class.forName(driverClassName); /* * 3. 通過省下的三個參數(shù)調(diào)用DriverManger的getConnection(),得到連接 */ Connection con = DriverManager.getConnection(url, username, password); /* * 二、得到Statement,執(zhí)行select語句 * 1. 得到Statement對象:Connection的createStatement()方法 */ Statement stmt = con.createStatement(); /* * 2. 調(diào)用Statement的ResultSet rs = executeQuery(String querySql) */ ResultSet rs = stmt.executeQuery("select * from emp"); /* * 三、解析ResultSet * 1. 把行光標(biāo)移動到第一行,可以調(diào)用next()方法完成! */ while(rs.next()) {//把光標(biāo)向下移動一行,并判斷下一行是否存在! int empno = rs.getInt(1);//通過列編號來獲取該列的值! String ename = rs.getString("ename");//通過列名稱來獲取該列的值 double sal = rs.getDouble("sal"); System.out.println(empno + ", " + ename + ", " + sal); } /* * 四、關(guān)閉資源 * 倒關(guān) */ rs.close(); stmt.close(); con.close();//這個東東,必須要關(guān),不關(guān)就死! }
4.9規(guī)范化代碼
// 規(guī)范化 @Test public void fun3() throws Exception { Connection con = null;//定義引用 Statement stmt = null; ResultSet rs = null; try { /* * 一、得到連接 */ String driverClassName = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://localhost:3306/exam"; String username = "root"; String password = "123"; Class.forName(driverClassName); con = DriverManager.getConnection(url, username, password);//實例化 /* * 二、創(chuàng)建Statement */ stmt = con.createStatement(); String sql = "select * from emp"; rs = stmt.executeQuery(sql); rs.last();//把光標(biāo)移動到最后一行 rs.beforeFirst(); /* * 三、循環(huán)遍歷rs,打印其中數(shù)據(jù) * * getString()和getObject()是通用的! */ // while(rs.next()) { // System.out.println(rs.getObject(1) + ", " // + rs.getString("ename") + ", " + rs.getDouble("sal")); // } int count = rs.getMetaData().getColumnCount(); while(rs.next()) {//遍歷行 for(int i = 1; i <= count; i++) {//遍歷列 System.out.print(rs.getString(i)); if(i < count) { System.out.print(", "); } } System.out.println(); } } catch(Exception e) { throw new RuntimeException(e); } finally { // 關(guān)閉 if(rs != null) rs.close(); if(stmt != null) stmt.close(); if(con != null) con.close(); } } }
它是Statement接口的子接口
優(yōu)點(diǎn):防SQL攻擊,提高代碼的可讀性、可維護(hù)性,提高效率
使用:
給出SQL模板---所謂SQL模板就是有“?”的SQL語句,其中“?”就是參數(shù)
使用Connection的prepareStatement(String sql):即創(chuàng)建它時就讓它與一條SQL模板綁定;
調(diào)用PreparedStatement的setXXX()系列方法為問號設(shè)置值
調(diào)用executeUpdate()或executeQuery()方法,但要注意,調(diào)用沒有參數(shù)的方法;
代碼
/* * 一、得到PreparedStatement * 1. 給出sql模板:所有的參數(shù)使用?來替代 * 2. 調(diào)用Connection方法,得到PreparedStatement */ String sql = "select * from t_user where username=? and password=?"; PreparedStatement pstmt = con.prepareStatement(sql); /* * 二、為參數(shù)賦值 */ pstmt.setString(1, username);//給第1個問號賦值,值為username pstmt.setString(2, password);//給第2個問號賦值,值為password ResultSet rs = pstmt.executeQuery();//調(diào)用查詢方法,向數(shù)據(jù)庫發(fā)送查詢語句 pstmt.setString(1, "liSi"); pstmt.setString(2, "123"); pstmt.executeQuery();
工具代碼v1.0
dbconfig.properties
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb1?useUnicode=true&characterEncoding=UTF8 username=root password=123
public class JdbcUtils { private static Properties props = null; // 只在JdbcUtils類被加載時執(zhí)行一次! static { // 給props進(jìn)行初始化,即加載dbconfig.properties文件到props對象中 try { InputStream in = JdbcUtils.class.getClassLoader() .getResourceAsStream("dbconfig.properties");//配置文件路徑在src目錄下 props = new Properties(); props.load(in); } catch(IOException e) { throw new RuntimeException(e); } // 加載驅(qū)動類 try { Class.forName(props.getProperty("driverClassName")); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } // 獲取連接! public static Connection getConnection() throws SQLException { // 得到Connection return DriverManager.getConnection(props.getProperty("url"), props.getProperty("username"), props.getProperty("password")); } }
DAO(Data Access Object)模式就是寫一個類,把訪問數(shù)據(jù)庫的代碼封裝起來。DAO在數(shù)據(jù)庫與業(yè)務(wù)邏輯(Service)之間。
實體域,即操作的對象,例如我們操作的表是user表,那么就需要先寫一個User類;
DAO模式需要先提供一個DAO接口;
然后再提供一個DAO接口的實現(xiàn)類;
再編寫一個DAO工廠,Service通過工廠來獲取DAO實現(xiàn)。
修改項目:
1.把UserDao修改為接口,然后把原來的UserDao修改類名為UserDaoImpl
2.修改UserService中對UserDao的實例化:private UserDao userDao =DaoFactory.getUserDao()
3.創(chuàng)建DaoFactory,提供getUserDao()
代碼
工廠類/** * 通過配置文件得到dao實現(xiàn)類的名稱! * 通過類名稱,完成創(chuàng)建類對象!(反射完成的!) * @author cxf * */ public class DaoFactory { private static Properties props = null; static { // 加載配置文件內(nèi)容到props對象中 try { InputStream in = DaoFactory.class .getClassLoader().getResourceAsStream("dao.properties"); props = new Properties(); props.load(in); } catch(Exception e) { throw new RuntimeException(e); } } /** * 返回一個具體UserDao的實現(xiàn)類對象 * @return */ public static UserDao getUserDao() { /** * 給出一個配置文件,文件中給出UserDao接口的實現(xiàn)類名稱! * 我們這個方法,獲取實現(xiàn)類的類名,通過反射完成創(chuàng)建對象! */ /* * 得到dao實現(xiàn)類的名稱 */ String daoClassName = props.getProperty("cn.itcast.usermng.dao.UserDao"); /* * 通過反射來創(chuàng)建實現(xiàn)類的對象 */ try { Class clazz = Class.forName(daoClassName); return (UserDao)clazz.newInstance(); } catch(Exception e) { throw new RuntimeException(e); } } }
數(shù)據(jù)庫類型與java中類型的對應(yīng)關(guān)系:
DATE → java.sql.Date
TIME → java.sql.Time
TIMESTAMP → java.sql.Timestamp
領(lǐng)域?qū)ο螅╠omain)中的所有屬性不能出現(xiàn)java.sql包下的東西!即不能使用java.sql.Date;
ResultSet#getDate()返回的是java.sql.Date()
PreparedStatement#setDate(int, Date),其中第二個參數(shù)也是java.sql.Date
時間類型的轉(zhuǎn)換:
java.util.Date java.sql.Date、Time、Timestamp
把util的Date轉(zhuǎn)換成毫秒值
使用毫秒值創(chuàng)建sql的Date、Time、Timestamp
java.sql.Date、Time、Timestamp java.util.Date
這一步不需要處理了:因為java.sql.Date是java.util.Date;
java.util.Datedate = new java.util.Date();
longl = date.getTime();
java.sql.DatesqlDate = new java.sql.Date(l);
java.sql包下給出三個與數(shù)據(jù)庫相關(guān)的日期時間類型,分別是:
Date:表示日期,只有年月日,沒有時分秒。會丟失時間;
Time:表示時間,只有時分秒,沒有年月日。會丟失日期;
Timestamp:表示時間戳,有年月日時分秒,以及毫秒。
這三個類都是java.util.Date的子類。
把數(shù)據(jù)庫的三種時間類型賦給java.util.Date,基本不用轉(zhuǎn)換,因為這是把子類對象給父類的引用,不需要轉(zhuǎn)換。
java.sql.Date date = …
java.util.Date d = date;
java.sql.Time time = …
java.util.Date d = time;
java.sql.Timestamp timestamp = …
java.util.Date d = timestamp;
當(dāng)需要把java.util.Date轉(zhuǎn)換成數(shù)據(jù)庫的三種時間類型時,這就不能直接賦值了,這需要使用數(shù)據(jù)庫三種時間類型的構(gòu)造器。java.sql包下的Date、Time、TimeStamp三個類的構(gòu)造器都需要一個long類型的參數(shù),表示毫秒值。創(chuàng)建這三個類型的對象,只需要有毫秒值即可。我們知道java.util.Date有g(shù)etTime()方法可以獲取毫秒值,那么這個轉(zhuǎn)換也就不是什么問題了。
java.utl.Date d = new java.util.Date();
java.sql.Date date = newjava.sql.Date(d.getTime());//會丟失時分秒
Time time = new Time(d.getTime());//會丟失年月日
Timestamp timestamp = newTimestamp(d.getTime());
1 什么是大數(shù)據(jù)
所謂大數(shù)據(jù),就是大的字節(jié)數(shù)據(jù),或大的字符數(shù)據(jù)。標(biāo)準(zhǔn)SQL中提供了如下類型來保存大數(shù)據(jù)
標(biāo)準(zhǔn)SQL大數(shù)據(jù)類型
類型 |
長度 |
tinyblob |
28--1B(256B) |
blob |
216-1B(64K) |
mediumblob |
224-1B(16M) |
longblob |
232-1B(4G) |
tinyclob |
28--1B(256B) |
clob |
216-1B(64K) |
mediumclob |
224-1B(16M) |
longclob |
232-1B(4G) |
在mysql中沒有提供tinyclob、clob、mediumclob、longclob四種類型
而是使用tinytext,text,mediumtext,longtext
代碼
把mp3保存到數(shù)據(jù)庫中!
com.mysql.jdbc.PacketTooBigException: Packet for query is too large (9802817 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.
在my.ini中設(shè)置,在[mysqld]下添加max_allowed_packet=10M,例如:
[mysqld]
max_allowed_packet=64M
/** * 大數(shù)據(jù) * @author cxf * */ public class Demo4 { /** * 把mp3保存到數(shù)據(jù)庫中。 * @throws SQLException * @throws IOException * @throws FileNotFoundException */ @Test public void fun1() throws Exception { /* * 1. 得到Connection * 2. 給出sql模板,創(chuàng)建pstmt * 3. 設(shè)置sql模板中的參數(shù) * 4. 調(diào)用pstmt的executeUpdate()執(zhí)行 */ Connection con = JdbcUtils.getConnection(); String sql = "insert into tab_bin values(?,?,?)"; PreparedStatement pstmt = con.prepareStatement(sql); pstmt.setInt(1, 1); pstmt.setString(2, "流光飛舞.mp3"); /** * 需要得到Blob * 1. 我們有的是文件,目標(biāo)是Blob * 2. 先把文件變成byte[] * 3. 再使用byte[]創(chuàng)建Blob */ // 把文件轉(zhuǎn)換成byte[] byte[] bytes = IOUtils.toByteArray(new FileInputStream("F:/流光飛舞.mp3")); // 使用byte[]創(chuàng)建Blob Blob blob = new SerialBlob(bytes); // 設(shè)置參數(shù) pstmt.setBlob(3, blob); pstmt.executeUpdate(); } /** * 從數(shù)據(jù)庫讀取mp3 * @throws SQLException */ @Test public void fun2() throws Exception { /* * 1. 創(chuàng)建Connection */ Connection con = JdbcUtils.getConnection(); /* * 2. 給出select語句模板,創(chuàng)建pstmt */ String sql = "select * from tab_bin"; PreparedStatement pstmt = con.prepareStatement(sql); /* * 3. pstmt執(zhí)行查詢,得到ResultSet */ ResultSet rs = pstmt.executeQuery(); /* * 4. 獲取rs中名為data的列數(shù)據(jù) */ if(rs.next()) { Blob blob = rs.getBlob("data"); /* * 把Blob變成硬盤上的文件! */ /* * 1. 通過Blob得到輸入流對象 * 2. 自己創(chuàng)建
MySQL的批處理也需要通過參數(shù)來打開:rewriteBatchedStatements=true
批處理就是一批一批的處理,而不是一個一個的處理!
當(dāng)你有10條SQL語句要執(zhí)行時,一次向服務(wù)器發(fā)送一條SQL語句,這么做效率上很差!
處理的方案是使用批處理,即一次向服務(wù)器發(fā)送多條SQL語句,然后由服務(wù)器一次性處理。
批處理只針對更新(增、刪、改)語句,批處理沒有查詢什么事兒!
可以多次調(diào)用Statement類的addBatch(Stringsql)方法,
把需要執(zhí)行的所有SQL語句添加到一個“批”中,
然后調(diào)用Statement類的executeBatch()方法來執(zhí)行當(dāng)前“批”中的語句。
voidaddBatch(String sql):添加一條語句到“批”中;
int[]executeBatch():執(zhí)行“批”中所有語句。返回值表示每條語句所影響的行數(shù)據(jù);
voidclearBatch():清空“批”中的所有語句。
for(int i = 0; i < 10; i++) { String number = "S_10" + i; String name = "stu" + i; int age = 20 + i; String gender = i % 2 == 0 ? "male" : "female"; String sql = "insert into stu values('" + number + "', '" + name + "', " + age + ", '" + gender + "')"; stmt[內(nèi)部有個集合,用來裝載sql語句].addBatch(sql); } stmt.executeBatch[執(zhí)行批,即一次把批中的所有sql語句發(fā)送給服務(wù)器]();
當(dāng)執(zhí)行了“批”之后,“批”中的SQL語句就會被清空!也就是說,連續(xù)兩次調(diào)用executeBatch()相當(dāng)于調(diào)用一次!因為第二次調(diào)用時,“批”中已經(jīng)沒有SQL語句了。
還可以在執(zhí)行“批”之前,調(diào)用Statement的clearBatch()方法來清空“批”!
PreparedStatement的批處理有所不同,因為每個PreparedStatement對象都綁定一條SQL模板。
所以向PreparedStatement中添加的不是SQL語句,而是給“?”賦值。
/** * 批處理 * @author cxf * */ public class Demo5 { /** * pstmt對象內(nèi)部有集合 * 1. 用循環(huán)瘋狂向pstmt中添加sql參數(shù),它自己有模板,使用一組參數(shù)與模板罰沒可以匹配出一條sql語句 * 2. 調(diào)用它的執(zhí)行批方法,完成向數(shù)據(jù)庫發(fā)送! * @throws SQLException */ @Test public void fun5() throws SQLException { /* * pstmt: * > 添加參數(shù)到批中 * > 執(zhí)行批! */ Connection con = JdbcUtils.getConnection(); String sql = "INSERT INTO t_stu VALUES(?,?,?,?)"; PreparedStatement pstmt = con.prepareStatement(sql); // 瘋狂的添加參數(shù) for(int i = 0; i < 10000; i++) { pstmt.setInt(1, i+1); pstmt.setString(2, "stu_" + i); pstmt.setInt(3, i); pstmt.setString(4, i%2==0?"男":"女"); pstmt.addBatch();//添加批!這一組參數(shù)就保存到集合中了。 } long start = System.currentTimeMillis(); pstmt.executeBatch();//執(zhí)行批! long end = System.currentTimeMillis(); System.out.println(end - start);//412764, 301 } }
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com