人生需要总结

手写DAO框架(四)-SQL执行

-------前篇:手写DAO框架(三)-数据库连接---------

前言

通过上一篇写的方法,可以灵活的获取、释放数据库连接,拿到连接之后,我们就可以执行sql了!所以,本篇介绍的就是SQL执行器

SQL执行器主要功能就是执行sql,返回结果。如新增、更新、删除、查询,其中也涉及到了事务的开启、关闭、回滚。

这一层的定位是为接下来的DAO层提供底层支持,同时也支持自行编写SQL进行直接调用。

涉及技术

本篇主要涉及技术有:泛型、匿名内部类。

泛型

说到泛型,先简单的对比一下python和java,在python中,一个方法可以有多个同类型或者不同类型的返回值;而在java里面,方法的返回值类型必须是确定。

在java里面,如果返回多个同类型的,可以直接通过一个数组或者集合来做。如果要返回多个不同类型的,就只能定义一个包装类了。如果不用泛型,定义的这些包装类就只能适用于很少部分方法。但是如果用了泛型(其实也包装了继承和类的初始化),这个包装类就厉害,可以只关注数量,不关注具体类型。我之前没有想到这么用,是后来看《Java编程思想》的时候才发现的。

举个🌰:

包含两个类的TwoTuple:

 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:

 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         //1、如果默认是手动提交,说明外界修改了自动提交,即外界要自己控制提交、回滚操作,则后续的提交、回滚操作不主动触发。
171         //2、如果默认是自动提交,后续则触发提交、回滚操作
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层实现

 

 

 

posted @ 2019-06-22 11:38  水木桶  阅读(1005)  评论(0编辑  收藏  举报