手寫DAO框架(四)-SQL執行

-------前篇:手寫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個的需求,能夠繼續寫下來。

接下來就進入正式的代碼環節。

SQL執行器

同樣是面向接口編程,先定義接口

sql執行接口

 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執行接口定義以後,還須要定義一個包含事務功能的接口,以便使用事務的功能。

包含主動進行事務控制的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裏面有多繼承,只不過是接口之間)。

接口定義完成以後,就是具體實現了。

sql執行器實現

  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兩個接口,這樣隨便使用哪一個接口均可以。

 

至此,整個框架已經具有了初步的可用性。

--本文內容到這裏就結束了,歡迎指導交流--

 

下篇:手寫DAO框架(五)-DAO層實現

相關文章
相關標籤/搜索