jdbc預(yù)編譯對(duì)象原始的jdbc使用聲明對(duì)象進(jìn)行sql的執(zhí)行,對(duì)于執(zhí)行的sql參數(shù)使用字符串拼接的形式進(jìn)行添加 Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/teacher", "root", "root"); Statement statement = con.createStatement(); // 當(dāng)使用原始的jdbc進(jìn)行相應(yīng)操作時(shí),由前臺(tái)獲取相應(yīng)查詢參數(shù),保存至變量。準(zhǔn)備sql時(shí)將參數(shù) // 使用字符串拼接的方式進(jìn)行數(shù)據(jù)的拼接,這有時(shí)會(huì)導(dǎo)致一些問題 // 如果使用者知道并了解sql的語句,那么就可以使用sql注入的方式混亂查詢結(jié)果 // 從而導(dǎo)致系統(tǒng)的不穩(wěn)定 // 將id與name當(dāng)做賬號(hào)密碼進(jìn)行登錄操作 String studentid = "203213213"; String studentname = "231313' or studentname!='1"; // 登錄的sql語句就會(huì)變?yōu)?String sql = "select * from student where studentid=" studentid " and studentname= '" studentname "'"; //select * from student where studentid=203213213 and studentname= '231313' or studentname!='1' //輸入者可以猜測(cè)數(shù)據(jù)庫(kù)sql語句并將sql以數(shù)據(jù)的形式傳入 //如果使用字符串拼接的形式進(jìn)行sql的生成那么會(huì)導(dǎo)致結(jié)果不符 ResultSet rs = statement.executeQuery(sql); ArrayList<Student> list = new ArrayList<>(); while (rs.next()) {// 如果結(jié)果集對(duì)象中存在數(shù)據(jù) Student student = new Student(rs.getString("studentid"), rs.getString("studentname"), rs.getInt("studentage"), rs.getString("studentsex"), rs.getString("studentaddress"), rs.getInt("classid")); list.add(student); } con.close(); for (Student student : list) { System.out.println(student); } 為了解決sql注入的問題,需要使用sql預(yù)編譯對(duì)象 Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/teacher", "root", "root"); String studentid = "203213213"; String studentname = "231313' or studentname!='1"; // String sql = "select * from student where studentid=" studentid " // and studentname= '" studentname "'"; // 原始的jdbc先創(chuàng)建聲明對(duì)象在執(zhí)行時(shí)才將sql交由聲明對(duì)象執(zhí)行 // Statement statement = con.createStatement(); // ResultSet rs = statement.executeQuery(sql); // 預(yù)編譯對(duì)象是先準(zhǔn)備執(zhí)行的sql 將所有參數(shù)使用?占位符替換 String sql = "select * from student where studentid= ? and studentname= ?"; // 使用連接對(duì)象獲取預(yù)編譯對(duì)象并將sql進(jìn)行預(yù)編譯 PreparedStatement ps = con.prepareStatement(sql); // 為占位符進(jìn)行賦值 ps.setObject(1, studentid);// 為指定占位符賦值 ps.setObject(2, studentname); ResultSet rs = ps.executeQuery(); ArrayList<Student> list = new ArrayList<>(); while (rs.next()) {// 如果結(jié)果集對(duì)象中存在數(shù)據(jù) Student student = new Student(rs.getString("studentid"), rs.getString("studentname"), rs.getInt("studentage"), rs.getString("studentsex"), rs.getString("studentaddress"), rs.getInt("classid")); list.add(student); } con.close(); for (Student student : list) { System.out.println(student); } 事務(wù)在進(jìn)行數(shù)據(jù)庫(kù)操作時(shí),為了保證數(shù)據(jù)的安全以及減少錯(cuò)誤的發(fā)生,一般都會(huì)進(jìn)行事務(wù)的管理,數(shù)據(jù)庫(kù)在進(jìn)行操作時(shí),內(nèi)部也進(jìn)行了簡(jiǎn)單的事務(wù)管理,只不過在進(jìn)行更復(fù)雜操作時(shí),這種默認(rèn)的事務(wù)不足以進(jìn)行相應(yīng)需求的實(shí)現(xiàn),這個(gè)時(shí)候就需要我們手動(dòng)的進(jìn)行事務(wù)的管理 事務(wù)的ACID原則· ***事務(wù)的原子性( Atomicity):***一組事務(wù),要么成功;要么撤回。 · ***一致性 (Consistency):***事務(wù)執(zhí)行后,數(shù)據(jù)庫(kù)狀態(tài)與其他業(yè)務(wù)規(guī)則保持一致。如轉(zhuǎn)賬業(yè)務(wù),無論事務(wù)執(zhí)行成功否,參與轉(zhuǎn)賬的兩個(gè)賬號(hào)余額之和應(yīng)該是不變的。 · ***隔離性(Isolation):***事務(wù)獨(dú)立運(yùn)行。一個(gè)事務(wù)處理后的結(jié)果,影響了其他事務(wù),那么其他事務(wù)會(huì)撤回。事務(wù)的100%隔離,需要犧牲速度。 · ***持久性(Durability):***軟、硬件崩潰后,InnoDB數(shù)據(jù)表驅(qū)動(dòng)會(huì)利用日志文件重構(gòu)修改??煽啃院透咚俣炔豢杉娴?, innodb_flush_log_at_trx_commit 選項(xiàng) 決定什么時(shí)候吧事務(wù)保存到日志里。 mysql數(shù)據(jù)庫(kù)中 使用sql語句進(jìn)行事務(wù)處理#在執(zhí)行多條sql前手動(dòng)開 啟事務(wù) start transaction; update student set studentname='Bill' where studentid='2010005'; update student set studentage=88 where studentid='2010005'; #在sql執(zhí)行結(jié)束后手動(dòng)提交事務(wù) commit; #mysql中如果事務(wù)在執(zhí)行過程中發(fā)生異常導(dǎo)致事務(wù)執(zhí)行出現(xiàn)問題,會(huì)自動(dòng)進(jìn)行回滾 #將已經(jīng)執(zhí)行的更改進(jìn)行撤銷 rollback; jdbc中進(jìn)行事務(wù)處理jdbc進(jìn)行事務(wù)處理的語法 獲取連接對(duì)象con 設(shè)置con.setAutoCommit(false);//關(guān)閉事務(wù)的自動(dòng)提交-》開啟事務(wù) 執(zhí)行多條sql 對(duì)執(zhí)行sql語句進(jìn)行捕獲如果出現(xiàn)異常則con.rollback();回滾事務(wù) 執(zhí)行成功則 con.commit(); 提交事務(wù) // mysql默認(rèn)開啟事務(wù)的自動(dòng)提交 // 每執(zhí)行1條SQL語句都是一個(gè)事務(wù) Class.forName("com.mysql.jdbc.Driver"); Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/teacher", "root", "root"); try { // 默認(rèn)事務(wù)自動(dòng)提交 每執(zhí)行1條sql一個(gè)事務(wù) 其他事務(wù)不會(huì)影響該事務(wù)的執(zhí)行 String sql = "update student set studentname='Bill' where studentid='2010005' "; // 事務(wù)的開啟 // 在執(zhí)行sql之前設(shè)置當(dāng)前連接 // 將自動(dòng)提交關(guān)閉 con.setAutoCommit(false); PreparedStatement ps = con.prepareStatement(sql); ps.executeUpdate(); // 第二條sql語句報(bào)錯(cuò)沒有執(zhí)行成功 但是第一條數(shù)據(jù)已經(jīng)執(zhí)行成功存入數(shù)據(jù)庫(kù) String sql2 = "update student set studesntage=28 where studentid='2010005' "; PreparedStatement ps2 = con.prepareStatement(sql2); ps2.executeUpdate(); } catch (Exception e) { // 對(duì)執(zhí)行的sql語句進(jìn)程try catch 如果出現(xiàn)異?;貪L事務(wù) con.rollback(); } // 如果執(zhí)行成功則提交事務(wù)將執(zhí)行的結(jié)果保存 con.commit(); 事務(wù)的隔離級(jí)別基于事務(wù)的隔離性,事務(wù)直接獨(dú)立互不影響,但也是取決于對(duì)不同數(shù)據(jù)的操作,如果多個(gè)事務(wù)對(duì)同一個(gè)數(shù)據(jù)進(jìn)行操作可能造成類似于java多線程操作同一變量的問題,從而導(dǎo)致數(shù)據(jù)的不準(zhǔn)確 因?yàn)椴l(fā)事務(wù)導(dǎo)致的問題大致有5類,其中兩類是更新問題三類是讀問題。 · 臟讀(dirty read):讀到另一個(gè)事務(wù)的未提交新數(shù)據(jù),即讀取到了臟數(shù)據(jù); · 不可重復(fù)讀(unrepeatable):對(duì)同一記錄的兩次讀取不一致,因?yàn)榱硪皇聞?wù)對(duì)該記錄做了修改; · 幻讀(虛讀)(phantom read):對(duì)同一張表的兩次查詢不一致,因?yàn)榱硪皇聞?wù)插入了一條記錄。 四大隔離級(jí)別4個(gè)等級(jí)的事務(wù)隔離級(jí)別,在相同的數(shù)據(jù)環(huán)境下,使用相同的輸入,執(zhí)行相同的工作,根據(jù)不同的隔離級(jí)別,可以導(dǎo)致不同的結(jié)果。不同事務(wù)隔離級(jí)別能夠解決的數(shù)據(jù)并發(fā)問題的能力是不同的。 1、SERIALIZABLE(串行化) 所有事務(wù)串行依次執(zhí)行,不會(huì)發(fā)生任何數(shù)據(jù)問題,但是效率最低 2、REPEATABLE READ(可重復(fù)讀)(MySQL) mysql默認(rèn)的事務(wù)隔離級(jí)別,事務(wù)之間在進(jìn)行操作時(shí),對(duì)操作的數(shù)據(jù)進(jìn)行限制,不允許其他事務(wù)對(duì)當(dāng)前事務(wù)正在處理的數(shù)據(jù)(行)進(jìn)行操作,可以避免臟讀和不可重復(fù)讀,效率比串行化高一點(diǎn) 3、READ COMMITTED(讀已提交數(shù)據(jù))(Oracle) oracle數(shù)據(jù)庫(kù)默認(rèn)的事務(wù)隔離級(jí)別,多個(gè)事務(wù)進(jìn)行執(zhí)行,除了可以任意讀取操作其他事務(wù)沒有處理的數(shù)據(jù)外,只能讀取其他事務(wù)已經(jīng)提交了的數(shù)據(jù),可以防止臟讀,但是不能防止不可重復(fù)讀與幻讀,效率比可重復(fù)讀高一點(diǎn) 4、READ UNCOMMITTED(讀未提交數(shù)據(jù)) 不進(jìn)行任何處理,不能解決任何隔離問題,但效率最高 mysql設(shè)置隔離級(jí)別MySQL的默認(rèn)隔離級(jí)別為Repeatable read,可以通過下面語句查看: SELECT @@ 也可以通過下面語句來設(shè)置當(dāng)前連接的隔離級(jí)別: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ ;//[4選1] JDBC設(shè)置隔離級(jí)別con.setTransactionIsolation(int level) :參數(shù)可選值如下: · Connection.TRANSACTION_READ_UNCOMMITTED; · Connection.TRANSACTION_READ_COMMITTED; · Connection.TRANSACTION_REPEATABLE_READ; · Connection.TRANSACTION_READ_SERIALIZABLE。 c3p0數(shù)據(jù)庫(kù)連接池的使用數(shù)據(jù)庫(kù)連接池:通過配置在程序執(zhí)行時(shí)直接創(chuàng)建多個(gè)數(shù)據(jù)庫(kù)連接并進(jìn)行保存,當(dāng)用戶需要使用連接時(shí)調(diào)用相應(yīng)方法獲取連接,連接的聲生命周期交由連接池控制。 將與數(shù)據(jù)庫(kù)的連接交由連接池進(jìn)行管理,我們只需要在使用時(shí)獲取連接進(jìn)行使用,之后連接池會(huì)自動(dòng)回收相應(yīng)的連接 1、導(dǎo)入c3p0的jar包 注意:c3p0額外依賴另一個(gè)jar包 所以在導(dǎo)入時(shí)需要導(dǎo)入兩個(gè)jar包 2、書寫連接池配置文件 <?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <!-- 這是默認(rèn)配置信息 --> <default-config> <!-- 連接四大參數(shù)配置 --> <property name="jdbcUrl">jdbc:mysql://localhost:3306/teacher?characterEncoding=UTF-8</property> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="user">root</property> <property name="password">root</property> <!-- 池參數(shù)配置 --> <!-- 如果池中數(shù)據(jù)連接不夠時(shí)一次增長(zhǎng)多少個(gè) --> <property name="acquireIncrement">5</property> <!-- 初始化數(shù)據(jù)庫(kù)連接池時(shí)連接的數(shù)量 --> <property name="initialPoolSize">20</property> <!-- 數(shù)據(jù)庫(kù)連接池中的最大的數(shù)據(jù)庫(kù)連接數(shù) --> <property name="maxPoolSize">25</property> <!-- 數(shù)據(jù)庫(kù)連接池中的最小的數(shù)據(jù)庫(kù)連接數(shù) --> <property name="minPoolSize">5</property> </default-config> </c3p0-config> 3、創(chuàng)建c3p0對(duì)象進(jìn)行使用 import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import com.mchange.v2.c3p0.ComboPooledDataSource; public class C3P0Test { final static ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); // 使用c3p0數(shù)據(jù)庫(kù)連接池獲取連接 public static void main(String[] args) throws SQLException { //C3P0獲取連接代碼(默認(rèn)獲取src下c3p0-config.xml) Connection connection = comboPooledDataSource.getConnection(); String sql="select * from student"; PreparedStatement ps = connection.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while(rs.next()){ System.out.println(rs.getString(1) "|" rs.getString(2)); } } } JDBC工具類的集成//根據(jù)預(yù)編譯對(duì)象預(yù)編譯的sql語句獲取結(jié)果元數(shù)據(jù) //包含返回?cái)?shù)據(jù)所有列的相應(yīng)信息(沒有行) ResultSetMetaData metaData = ps.getMetaData(); //獲取返回?cái)?shù)據(jù)的列數(shù) int columnCount = metaData.getColumnCount(); //獲取元數(shù)據(jù)指定列的名字 String columnLabel = metaData.getColumnLabel(2); import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.ArrayList; import com.mchange.v2.c3p0.ComboPooledDataSource; //數(shù)據(jù)庫(kù)連接工具類 public class AiDButil { // 注意:本工具類在使用前需要確?;A(chǔ)準(zhǔn)備工作已完成 // 1 mysql c3p0共 3個(gè)jar包(log4j也可以添加) // 2 c3p0配置文件配置相應(yīng)屬性(如果導(dǎo)入了log4j那么也需要配置log4j配置文件) // 3 明確調(diào)用方法填入相應(yīng)參數(shù) // (1)查詢語句返回集合泛型類型屬性必須與數(shù)據(jù)庫(kù)列名一致(能多不能少) // (2)不確定參數(shù)填入的參數(shù)與sql中占位符?一致 且填入順序一致 // 需要導(dǎo)入mysql連接jar包與c3p0使用的兩個(gè)jar包 final static ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource(); // 獲取連接方法 // 返回一個(gè)連接對(duì)象 public static Connection getCon() { // 連接使用c3p0進(jìn)行獲取 // 使用c3p0數(shù)據(jù)庫(kù)連接池獲取連接 Connection connection = null; try { connection = comboPooledDataSource.getConnection(); } catch (SQLException e) { System.err.println("獲取連接失敗"); return null; } return connection; } // DML方法 // 不支持事務(wù) 單條sql語句執(zhí)行 public static boolean DML(String sql, Object... o) { // 獲取連接 Connection con = getCon(); // 創(chuàng)建預(yù)編譯對(duì)象 try { PreparedStatement ps = con.prepareStatement(sql); for (int i = 0; i < o.length; i ) { ps.setObject((i 1), o[i]); } ps.executeUpdate(); } catch (SQLException e) { System.out.println("查詢執(zhí)行失敗:" sql); return false; } return true; } // DML方法 // 支持事務(wù) 多條sql語句執(zhí)行 public static boolean DML(Connection con, String sql, Object... o) { // 創(chuàng)建預(yù)編譯對(duì)象 try { PreparedStatement ps = con.prepareStatement(sql); for (int i = 0; i < o.length; i ) { ps.setObject((i 1), o[i]); } ps.executeUpdate(); } catch (SQLException e) { System.out.println("查詢執(zhí)行失敗:" sql); return false; } return true; } // 查詢dql語句方法 public static <E> ArrayList<E> DQL(String sql, Class<E> c, Object... o) { ArrayList<E> list = new ArrayList<>(); try { // 獲取連接 Connection con = getCon(); // 準(zhǔn)備預(yù)編譯對(duì)象 PreparedStatement ps = con.prepareStatement(sql); // 獲取元數(shù)據(jù) 準(zhǔn)備存儲(chǔ)所有列名的數(shù)組 ResultSetMetaData metaData = ps.getMetaData(); // 創(chuàng)建指定長(zhǎng)度用于存儲(chǔ)列名的數(shù)組 String[] names = new String[metaData.getColumnCount()]; // 循環(huán)為names數(shù)組進(jìn)行賦值 for (int i = 0; i < names.length; i ) { names[i] = metaData.getColumnLabel(i 1);// 獲取指定列名 } // 執(zhí)行sql返回結(jié)果集 ResultSet rs = ps.executeQuery(); while (rs.next()) { // 每一行數(shù)據(jù)就是一個(gè)對(duì)象 // 使用反射創(chuàng)建對(duì)象 E obj = c.newInstance(); // 當(dāng)前行所有列名 在names數(shù)組中存儲(chǔ) // 循環(huán)names數(shù)組取出當(dāng)前行對(duì)應(yīng)數(shù)據(jù) for (String colname : names) { Object value = rs.getObject(colname);// 獲取列名對(duì)應(yīng)值 // 將值存入相應(yīng)對(duì)象 // 使用反射獲取類中同名的屬性對(duì)象 Field field = c.getDeclaredField(colname); // 私有屬性使用前必須賦權(quán) field.setAccessible(true); // 調(diào)用屬性對(duì)象的set方法為指定對(duì)象進(jìn)行賦值 field.set(obj, value); } // 列名循環(huán)結(jié)束后對(duì)應(yīng)對(duì)象屬性已經(jīng)全部進(jìn)行賦值 // 將對(duì)象存儲(chǔ)至集合中 list.add(obj); } } catch (Exception e) { e.printStackTrace(); return null; } return list; } }來源:https://www./content-2-784751.html |
|