-------前篇:手寫DAO框架(三)-數據庫鏈接---------html
經過上一篇寫的方法,能夠靈活的獲取、釋放數據庫鏈接,拿到鏈接以後,咱們就能夠執行sql了!因此,本篇介紹的就是SQL執行器。java
SQL執行器主要功能就是執行sql,返回結果。如新增、更新、刪除、查詢,其中也涉及到了事務的開啓、關閉、回滾。python
這一層的定位是爲接下來的DAO層提供底層支持,同時也支持自行編寫SQL進行直接調用。sql
本篇主要涉及技術有:泛型、匿名內部類。數據庫
說到泛型,先簡單的對比一下python和java,在python中,一個方法能夠有多個同類型或者不一樣類型的返回值;而在java裏面,方法的返回值類型必須是肯定。編程
在java裏面,若是返回多個同類型的,能夠直接經過一個數組或者集合來作。若是要返回多個不一樣類型的,就只能定義一個包裝類了。若是不用泛型,定義的這些包裝類就只能適用於不多部分方法。可是若是用了泛型(其實也包裝了繼承和類的初始化),這個包裝類就厲害,能夠只關注數量,不關注具體類型。我以前沒有想到這麼用,是後來看《Java編程思想》的時候才發現的。數組
舉個🌰:框架
包含兩個類的TwoTuple:ide
1 package me.lovegao.gdao.bean; 2 3 public class TwoTuple<A, B> { 4 public final A a; 5 public final B b; 6 7 public TwoTuple(A a, B b) { 8 this.a = a; 9 this.b = b; 10 } 11 }
包含三個類的ThreeTuple:post
1 package me.lovegao.gdao.bean; 2 3 public class ThreeTuple<A, B, C> extends TwoTuple<A, B> { 4 public final C c; 5 6 public ThreeTuple(A a, B b, C c) { 7 super(a, b); 8 this.c = c; 9 } 10 }
若是有4個、5個的需求,能夠繼續寫下來。
接下來就進入正式的代碼環節。
同樣是面向接口編程,先定義接口
1 package me.lovegao.gdao.sqlexecute; 2 3 import java.util.List; 4 5 import me.lovegao.gdao.bean.TwoTuple; 6 7 /** 8 * sql執行接口 9 * @author simple 10 * 11 */ 12 public interface ISqlExecutor { 13 /** 14 * 查詢 15 * @param sql sql語句 16 * @param params 參數值 17 * @return 值列表 18 */ 19 List<Object[]> query(String sql, Object[] params) throws Exception; 20 21 /** 22 * 查詢值,和對應的數據庫列名 23 * @param sql 24 * @param params 25 * @return 26 * @throws Exception 27 */ 28 TwoTuple<List<Object[]>, String[]> queryValueAndColumn(String sql, Object[] params) throws Exception; 29 30 /** 31 * 插入數據 32 * @param sql 插入sql 33 * @param params 參數值 34 * @return 插入數據的id 35 */ 36 <T> T insert(String sql, Object[] params) throws Exception; 37 38 /** 39 * 批量插入 40 * @param sql 41 * @param paramsList 42 * @return 43 */ 44 int[] insertOrUpdateBatch(String sql, List<Object[]> paramsList) throws Exception; 45 46 /** 47 * 更新數據 48 * @param sql 49 * @param params 50 * @return 51 */ 52 int update(String sql, Object[] params) throws Exception; 53 54 }
sql執行接口定義以後,還須要定義一個包含事務功能的接口,以便使用事務的功能。
1 package me.lovegao.gdao.sqlexecute; 2 3 /** 4 * 包含主動進行事務控制的sql執行器 5 * @author simple 6 * 7 */ 8 public interface IManulTransactionSqlExecutor extends ISqlExecutor { 9 /** 10 * 開啓事務 11 * @throws Exception 12 */ 13 public void beginTransaction() throws Exception; 14 15 /** 16 * 開啓事務 17 * @param transactionIsolationLevel 事務傳播級別 one of the following <code>Connection</code> constants: 18 * <code>Connection.TRANSACTION_READ_UNCOMMITTED</code>, 19 * <code>Connection.TRANSACTION_READ_COMMITTED</code>, 20 * <code>Connection.TRANSACTION_REPEATABLE_READ</code>, or 21 * <code>Connection.TRANSACTION_SERIALIZABLE</code>. 22 * (Note that <code>Connection.TRANSACTION_NONE</code> cannot be used 23 * because it specifies that transactions are not supported.) 24 * @throws Exception 25 */ 26 public void beginTransaction(int transactionIsolationLevel) throws Exception; 27 28 /** 29 * 提交事務 30 * @throws Exception 31 */ 32 public void commitTransaction() throws Exception; 33 34 /** 35 * 回滾事務 36 * @throws Exception 37 */ 38 public void rollbackTransaction() throws Exception; 39 }
由於進行事務控制的同時,還得須要sql執行,因此就採起了接口繼承(其實Java裏面有多繼承,只不過是接口之間)。
接口定義完成以後,就是具體實現了。
1 package me.lovegao.gdao.sqlexecute; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import java.sql.ResultSet; 6 import java.sql.ResultSetMetaData; 7 import java.sql.Statement; 8 import java.util.ArrayList; 9 import java.util.List; 10 11 import me.lovegao.gdao.bean.TwoTuple; 12 import me.lovegao.gdao.connection.IConnectionPool; 13 import me.lovegao.gdao.util.GDaoCommonUtil; 14 import me.lovegao.gdao.util.JDBCUtil; 15 16 /** 17 * sql執行類 18 * @author simple 19 * 20 */ 21 public class SimpleSqlExecutor implements ISqlExecutor, IManulTransactionSqlExecutor { 22 private ThreadLocal<Connection> CONNECTION_THREAD_LOCAL = new ThreadLocal(); 23 private IConnectionPool connectionPool; 24 /**默認事務隔離級別**/ 25 private static int DEFAULT_TRANSACTION_ISOLATION_LEVEL = Connection.TRANSACTION_READ_COMMITTED; 26 /**查詢超時時間-秒**/ 27 private int queryTimeoutSeconds; 28 29 public SimpleSqlExecutor(IConnectionPool connectionPool) { 30 this.connectionPool = connectionPool; 31 this.queryTimeoutSeconds = connectionPool.getQueryTimeoutSecond(); 32 } 33 34 //獲取鏈接 35 private Connection getConnection() throws Exception { 36 Connection conn = CONNECTION_THREAD_LOCAL.get(); 37 if(conn == null) { 38 conn = connectionPool.getConnection(); 39 conn.setTransactionIsolation(DEFAULT_TRANSACTION_ISOLATION_LEVEL); 40 conn.setAutoCommit(true); 41 CONNECTION_THREAD_LOCAL.set(conn); 42 } 43 return conn; 44 } 45 46 //釋放鏈接 47 private void releaseConnection() { 48 Connection conn = CONNECTION_THREAD_LOCAL.get(); 49 if(conn != null) { 50 CONNECTION_THREAD_LOCAL.remove(); 51 connectionPool.returnConnection(conn); 52 } 53 } 54 55 /** 56 * 通用sql執行 57 * @param sql 58 * @param params 59 * @param preparedStatementResolve 60 * @return 61 * @throws Exception 62 */ 63 private <T> T generalSqlExecute(String sql, Object[] params, boolean returnKeys, 64 IPreparedStatementResolve<T> preparedStatementResolve) throws Exception { 65 T t = null; 66 Connection conn = null; 67 PreparedStatement ps = null; 68 try { 69 conn = getConnection(); 70 if(returnKeys) { 71 ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); 72 } else { 73 ps = conn.prepareStatement(sql); 74 } 75 ps.setQueryTimeout(queryTimeoutSeconds); 76 if(!GDaoCommonUtil.checkCollectionEmpty(params)) { 77 for(int i=0; i<params.length; i++) { 78 ps.setObject(i+1, params[i]); 79 } 80 } 81 t = preparedStatementResolve.solvePreparedStatement(ps); 82 } catch (Exception e) { 83 throw e; 84 } finally { 85 JDBCUtil.closePreparedStatement(ps); 86 releaseConnection(); 87 } 88 return t; 89 } 90 91 /** 92 * 查詢 93 * @param sql 查詢sql 94 * @param params sql中?對應的值 95 * @param needColumn 是否須要列名 96 * @return 97 * @throws Exception 98 */ 99 private TwoTuple<List<Object[]>, String[]> query(String sql, Object[] params, boolean needColumnName) throws Exception { 100 TwoTuple<List<Object[]>, String[]> tuple = generalSqlExecute(sql, params, false, new IPreparedStatementResolve<TwoTuple<List<Object[]>, String[]>>() { 101 @Override 102 public TwoTuple<List<Object[]>, String[]> solvePreparedStatement(PreparedStatement ps) throws Exception { 103 List<Object[]> localList = new ArrayList(); 104 String[] columns = null; 105 ResultSet rs = ps.executeQuery(); 106 ResultSetMetaData metaData = rs.getMetaData(); 107 int columnCount = metaData.getColumnCount(); 108 if(needColumnName) { 109 columns = new String[columnCount]; 110 for(int i=1; i<=columnCount; i++) { 111 columns[i-1] = metaData.getColumnName(i); 112 } 113 } 114 while(rs.next()) { 115 Object[] rowData = new Object[columnCount]; 116 for(int i=1; i<=columnCount; i++) { 117 rowData[i-1] = rs.getObject(i); 118 } 119 localList.add(rowData); 120 } 121 JDBCUtil.closeResultSet(rs); 122 return new TwoTuple<List<Object[]>, String[]>(localList, columns); 123 } 124 }); 125 return tuple; 126 } 127 128 @Override 129 public List<Object[]> query(String sql, Object[] params) throws Exception { 130 List<Object[]> list = new ArrayList(); 131 TwoTuple<List<Object[]>, String[]> tuple = query(sql, params, false); 132 if(tuple != null) { 133 list = tuple.a; 134 } 135 return list; 136 } 137 138 @Override 139 public TwoTuple<List<Object[]>, String[]> queryValueAndColumn(String sql, Object[] params) throws Exception { 140 TwoTuple<List<Object[]>, String[]> tuple = query(sql, params, true); 141 return tuple; 142 } 143 144 145 @Override 146 public <T> T insert(String sql, Object[] params) throws Exception { 147 T pk = null; 148 pk = generalSqlExecute(sql, params, true, new IPreparedStatementResolve<T>() { 149 @Override 150 public T solvePreparedStatement(PreparedStatement ps) throws Exception { 151 T id = null; 152 ps.executeUpdate(); 153 ResultSet rs = ps.getGeneratedKeys(); 154 if(rs.next()) { 155 id = (T) rs.getObject(1); 156 } 157 JDBCUtil.closeResultSet(rs); 158 return id; 159 } 160 }); 161 return pk; 162 } 163 164 @Override 165 public int[] insertOrUpdateBatch(String sql, List<Object[]> paramsList) throws Exception { 166 int[] res = null; 167 Connection conn = null; 168 PreparedStatement ps = null; 169 //是否改變了自動提交,防止此事務外還有其餘事務 170 //一、若是默認是手動提交,說明外界修改了自動提交,即外界要本身控制提交、回滾操做,則後續的提交、回滾操做不主動觸發。 171 //二、若是默認是自動提交,後續則觸發提交、回滾操做 172 boolean changeAutoCommit = false; 173 try { 174 conn = getConnection(); 175 //默認是主動提交 176 if(changeAutoCommit = conn.getAutoCommit()) { 177 //改成非自動提交 178 conn.setAutoCommit(false); 179 } 180 ps = conn.prepareStatement(sql); 181 ps.setQueryTimeout(queryTimeoutSeconds); 182 if(!GDaoCommonUtil.checkCollectionEmpty(paramsList)) { 183 for(Object[] params : paramsList) { 184 for(int i=0; i<params.length; i++) { 185 ps.setObject(i+1, params[i]); 186 } 187 ps.addBatch(); 188 } 189 } 190 res = ps.executeBatch(); 191 if(changeAutoCommit) { 192 conn.commit(); 193 } 194 } catch (Exception e) { 195 if(changeAutoCommit) { 196 conn.rollback(); 197 } 198 throw e; 199 } finally { 200 if(ps != null) { 201 ps.close(); 202 } 203 if(conn != null && changeAutoCommit) { 204 conn.setAutoCommit(true); 205 releaseConnection(); 206 } 207 } 208 return res; 209 } 210 211 @Override 212 public int update(String sql, Object[] params) throws Exception { 213 int tmpRes = generalSqlExecute(sql, params, false, new IPreparedStatementResolve<Integer>() { 214 @Override 215 public Integer solvePreparedStatement(PreparedStatement ps) throws Exception { 216 int tmpRes = ps.executeUpdate(); 217 return tmpRes; 218 } 219 }); 220 return tmpRes; 221 } 222 223 @Override 224 public void beginTransaction() throws Exception { 225 beginTransaction(DEFAULT_TRANSACTION_ISOLATION_LEVEL); 226 } 227 228 @Override 229 public void beginTransaction(int transactionIsolationLevel) throws Exception { 230 Connection conn = getConnection(); 231 conn.setAutoCommit(false); 232 conn.setTransactionIsolation(transactionIsolationLevel); 233 } 234 235 @Override 236 public void commitTransaction() throws Exception { 237 Connection conn = getConnection(); 238 conn.commit(); 239 conn.setAutoCommit(true); 240 releaseConnection(); 241 } 242 243 @Override 244 public void rollbackTransaction() throws Exception { 245 Connection conn = getConnection(); 246 conn.rollback(); 247 conn.setAutoCommit(true); 248 releaseConnection(); 249 } 250 251 }
實現了ISqlExecutor, IManulTransactionSqlExecutor兩個接口,這樣隨便使用哪一個接口均可以。
至此,整個框架已經具有了初步的可用性。
--本文內容到這裏就結束了,歡迎指導交流--