個人知識管理系統Version1.0開發記錄(10)


物理分頁

      這次我們運用Mybatis攔截器來實現物理分頁,后面會運用動態sql來實現,或者運用Map/CollectionUtils/StringUtils編寫工具類來實現。oracle是運用的rownum,mysql是運用的limit offset,pagesize。代碼中有大量注釋,可以參考Mybatis基本原理一起閱讀。后面,我們會根據一些實際開發需要,把物理分頁功能的代碼封裝成jar包,以后直接調用就好了,比如Mybatis+Spring3的運行環境,可以采用Mybatis動態sql來實現分頁,也可以采用Mybatis插件。

1、項目備份。一定要勤於備份。我們永遠不知道我們什么時候,會把早上還跑得通的一個項目,到晚上都還找不到哪里被玩壞了。是的,我們在編寫控制層的時候,交互使用了struts2和springmvc,想看看他們之間的差異,然后,就迷糊了,由於忘記備份struts2版本的,到了晚上就只好還原到昨天的去了。很暈很暈。

2、Mybatis插件類,實現攔截器,實現sql重寫。每一個攔截器必須實現三個方法。

  1 package com.dyl.util;
  2 
  3 import java.lang.reflect.Field;
  4 import java.sql.Connection;
  5 import java.sql.PreparedStatement;
  6 import java.sql.ResultSet;
  7 import java.sql.SQLException;
  8 import java.util.List;
  9 import java.util.Map;
 10 import java.util.Properties;
 11 
 12 import javax.xml.bind.PropertyException;
 13 
 14 import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
 15 import org.apache.ibatis.executor.ErrorContext;
 16 import org.apache.ibatis.executor.ExecutorException;
 17 import org.apache.ibatis.executor.statement.BaseStatementHandler;
 18 import org.apache.ibatis.executor.statement.RoutingStatementHandler;
 19 import org.apache.ibatis.executor.statement.StatementHandler;
 20 import org.apache.ibatis.mapping.BoundSql;
 21 import org.apache.ibatis.mapping.MappedStatement;
 22 import org.apache.ibatis.mapping.ParameterMapping;
 23 import org.apache.ibatis.mapping.ParameterMode;
 24 import org.apache.ibatis.plugin.Interceptor;
 25 import org.apache.ibatis.plugin.Intercepts;
 26 import org.apache.ibatis.plugin.Invocation;
 27 import org.apache.ibatis.plugin.Plugin;
 28 import org.apache.ibatis.plugin.Signature;
 29 import org.apache.ibatis.reflection.MetaObject;
 30 import org.apache.ibatis.reflection.property.PropertyTokenizer;
 31 import org.apache.ibatis.session.Configuration;
 32 import org.apache.ibatis.type.TypeHandler;
 33 import org.apache.ibatis.type.TypeHandlerRegistry;
 34 /**
 35  * 攔截器簽名
 36  * 要攔截的目標類型是StatementHandler(注意:type只能配置成接口類型),攔截的方法是,名稱為prepare,參數為Connection類型的方法。
 37  * @author dyl
 38  * @date 2014-7-12
 39  */
 40 @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
 41 public class PagePlugin implements Interceptor {
 42     /**
 43      * Mybatis采用責任鏈模式,通過動態代理組織多個攔截器(插件),通過這些攔截器可以改變Mybatis的默認行為(諸如SQL重寫之類的)。
 44      * 
 45      * 每一個攔截器都必須實現的三個方法。
 46      * (1)Object intercept(Invocation ivk),實現攔截邏輯的地方,內部要通過invocation.proceed()顯式地推進責任鏈前進,
 47      *  也就是調用下一個攔截器攔截目標方法。
 48      * (2)Object plugin(Object target),用當前這個攔截器生成對目標target的代理,實際是通過Plugin.wrap(target,this)來完成的,
 49      *  把目標target和攔截器this傳給了包裝函數。
 50      * (3)void setProperties(Properties p),設置額外的參數,參數配置在攔截器的Properties節點里。
 51      * 
 52      * Mybatis配置的插件,運行時發生。
 53      * (1)所有可能被攔截的處理類都會生成一個代理。
 54      * (2)處理類代理在執行對應方法時,判斷要不要執行插件中的攔截方法。
 55      * (3)執行插接中的攔截方法后,推進目標的執行。
 56      */
 57     private static String dialect = "";
 58     private static String pageSqlId = "";
 59 /*
 60  * intercept的實現
 61  * 實現攔截邏輯的地方,內部要通過invocation.proceed()顯式地推進責任鏈前進,也就是調用下一個攔截器攔截目標方法。
 62  * @see org.apache.ibatis.plugin.Interceptor#intercept(org.apache.ibatis.plugin.Invocation)
 63  */
 64     @SuppressWarnings("unchecked")
 65     public Object intercept(Invocation ivk) throws Throwable {
 66         /**
 67          * StatementHandler的默認實現類是RoutingStatementHandler,因此攔截的實際對象是它。
 68          * RoutingStatementHandler的主要功能是分發,它根據配置Statement類型創建真正執行數據庫操作的StatementHandler,並將其保存到delegate屬性里。
 69          * 由於delegate是一個私有屬性並且沒有提供訪問它的方法,因此需要借助ReflectHelper的幫忙。
 70          * 通過ReflectHelper的封裝后我們可以輕易的獲得想要的屬性。
 71          */
 72         if (ivk.getTarget() instanceof RoutingStatementHandler) {
 73             RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk.getTarget();
 74             BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler, "delegate");
 75             MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement");
 76             
 77             // 只重寫需要分頁的sql語句。通過MappedStatement的ID匹配,默認重寫以Page結尾的MappedStatement的sql。
 78             if (mappedStatement.getId().matches(pageSqlId)) {
 79                 BoundSql boundSql = delegate.getBoundSql();
 80                 Object parameterObject = boundSql.getParameterObject();
 81                 if (parameterObject == null) {
 82                     throw new NullPointerException("parameterObject error");
 83                 } else {
 84                     Connection connection = (Connection) ivk.getArgs()[0];
 85                     String sql = boundSql.getSql();
 86                     // 記錄總記錄數
 87                     String countSql = "select count(0) from (" + sql + ") myCount";
 88                     // System.out.println("總數sql 語句:" + countSql);
 89                     PreparedStatement countStmt = connection.prepareStatement(countSql);
 90                     BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
 91                             boundSql.getParameterMappings(), parameterObject);
 92                     // 重設分頁參數里的總頁數等
 93                     setParameters(countStmt, mappedStatement, countBS, parameterObject);
 94                     
 95                     ResultSet rs = countStmt.executeQuery();
 96                     int count = 0;
 97                     if (rs.next()) {
 98                         count = rs.getInt(1);
 99                     }
100                     rs.close();
101                     countStmt.close();
102                     
103                     // 分頁參數作為參數對象parameterObject的一個屬性
104                     PageInfo page = null;
105                     if (parameterObject instanceof PageInfo) {
106                         page = (PageInfo) parameterObject;
107                         page.setTotalResult(count);
108                     } else if (parameterObject instanceof Map) {
109                         Map<String, Object> map = (Map<String, Object>) parameterObject;
110                         page = (PageInfo) map.get("page");
111                         if (page == null)
112                             page = new PageInfo();
113                         page.setTotalResult(count);
114                     } else {
115                         Field pageField = ReflectHelper.getFieldByFieldName(parameterObject, "page");
116                         if (pageField != null) {
117                             page = (PageInfo) ReflectHelper.getValueByFieldName(parameterObject, "page");
118                             if (page == null)
119                                 page = new PageInfo();
120                             page.setTotalResult(count);
121                             ReflectHelper.setValueByFieldName(parameterObject, "page", page);
122                         } else {
123                             throw new NoSuchFieldException(parameterObject.getClass().getName());
124                         }
125                     }
126                     // 重寫sql
127                     String pageSql = generatePageSql(sql, page);
128                     System.out.println("page sql:" + pageSql);
129                     ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql);
130                 }
131             }
132         }
133         // 將執行權交給下一個攔截器,完成調用鏈的推進。
134         return ivk.proceed();
135     }
136     /**
137      * 重設分頁參數里的總頁數等
138      * @param ps
139      * @param mappedStatement
140      * @param boundSql
141      * @param parameterObject
142      * @throws SQLException
143      */
144     private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) 
145             throws SQLException {
146         ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
147         List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
148         if (parameterMappings != null) {
149             Configuration configuration = mappedStatement.getConfiguration();
150             TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
151             MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject);
152             for (int i = 0; i < parameterMappings.size(); i++) {
153                 ParameterMapping parameterMapping = parameterMappings.get(i);
154                 if (parameterMapping.getMode() != ParameterMode.OUT) {
155                     Object value;
156                     String propertyName = parameterMapping.getProperty();
157                     PropertyTokenizer prop = new PropertyTokenizer(propertyName);
158                     if (parameterObject == null) {
159                         value = null;
160                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
161                         value = parameterObject;
162                     } else if (boundSql.hasAdditionalParameter(propertyName)) {
163                         value = boundSql.getAdditionalParameter(propertyName);
164                     } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) 
165                             && boundSql.hasAdditionalParameter(prop.getName())) {
166                         value = boundSql.getAdditionalParameter(prop.getName());
167                         if (value != null) {
168                             value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
169                         }
170                     } else {
171                         value = metaObject == null ? null : metaObject.getValue(propertyName);
172                     }
173                     TypeHandler typeHandler = parameterMapping.getTypeHandler();
174                     if (typeHandler == null) {
175                         throw new ExecutorException("There was no TypeHandler found for parameter " + propertyName + " of statement "
176                                         + mappedStatement.getId());
177                     }
178                     typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
179                 }
180             }
181         }
182     }
183     /**
184      * sql重寫,在原始的sql語句上加入分頁的參數,目前支持mysql和oracle兩種數據庫的分頁。
185      * @param sql
186      * @param page
187      * @return
188      */
189     private String generatePageSql(String sql, PageInfo page) {
190         if (page != null && (dialect != null || !dialect.equals(""))) {
191             StringBuffer pageSql = new StringBuffer();
192             //StringBuilder pageSql = new StringBuilder();
193             if ("mysql".equals(dialect)) {
194                 //pageSql=generatePageSqlForMysql(sql,page);
195                 pageSql.append(sql);
196                 pageSql.append(" limit " + page.getCurrentResult() + "," + page.getShowCount());
197             } else if ("oracle".equals(dialect)) {
198                 //pageSql = generatePageSqlForOracle(sql, page);
199                 pageSql.append("select * from (select tmp_tb.*,ROWNUM row_id from (");
200                 pageSql.append(sql);
201                 pageSql.append(")  tmp_tb where ROWNUM<=");
202                 //pageSql.append(page.getCurrentResult() + page.getShowCount());
203                 pageSql.append(page.getCurrentPage()*page.getShowCount());
204                 pageSql.append(") where row_id>");
205                 //pageSql.append(page.getCurrentResult());
206                 pageSql.append((page.getCurrentPage() - 1) * page.getShowCount());
207             }
208             return pageSql.toString();
209         } else {
210             return sql;
211         }
212     }
213     /**
214      * mysql的分頁實現
215      * @param sql
216      * @param page
217      * @return
218      */
219     public StringBuilder generatePageSqlForMysql(String sql, PageInfo page) {
220         StringBuilder pageSql = new StringBuilder(100);
221         String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getShowCount());
222         pageSql.append(sql);
223         pageSql.append(" limit " + beginrow + "," + page.getShowCount());
224         return pageSql;
225     }
226     /**
227      * oracle的分頁實現
228      * @param sql
229      * @param page
230      * @return
231      */
232     public StringBuilder generatePageSqlForOracle(String sql, PageInfo page) {
233         StringBuilder pageSql = new StringBuilder(100);
234         String beginrow = String.valueOf((page.getCurrentPage() - 1) * page.getShowCount());
235         String endrow = String.valueOf(page.getCurrentPage() * page.getShowCount());
236         pageSql.append("select * from ( select temp.*, rownum row_id from ( ");
237         pageSql.append(sql);
238         pageSql.append(" ) temp where rownum <= ").append(endrow);
239         pageSql.append(") where row_id > ").append(beginrow);
240         return pageSql;
241     }
242     /**
243      * plugin的實現
244      * 用當前這個攔截器生成對目標target的代理,實際是通過Plugin.wrap(target,this)來完成的,把目標target和攔截器this傳給了包裝函數。
245      */
246     public Object plugin(Object target) {
247         // 當目標類是StatementHandler類型時,才包裝目標類,否者直接返回目標本身,減少目標被代理的次數。
248         if (target instanceof StatementHandler) {
249             return Plugin.wrap(target, this);
250         } else {
251             return target;
252         }
253     }
254     /**
255      * 設置額外的參數,參數配置在攔截器的Properties節點里。
256      */
257     public void setProperties(Properties p) {
258         dialect = p.getProperty("dialect");
259         if (dialect == null || dialect.equals("")) {
260             try {
261                 throw new PropertyException("dialect property is not found!");
262             } catch (PropertyException e) {
263                 e.printStackTrace();
264             }
265         }
266         pageSqlId = p.getProperty("pageSqlId");
267         if (dialect == null || dialect.equals("")) {
268             try {
269                 throw new PropertyException("pageSqlId property is not found!");
270             } catch (PropertyException e) {
271                 e.printStackTrace();
272             }
273         }
274     }
275 }
View Code

3、頁面實體類,把頁面相關屬性封裝成一個類,方便數據傳輸。----------------------------上面的屬性是用於攔截器的,下面的屬性是用於動態sql和工具類的。

  1 package com.dyl.util;
  2 
  3 import java.io.Serializable;
  4 import java.util.List;
  5 /**
  6  * 頁面
  7  * @author dyl
  8  * @date 2014-7-12
  9  */
 10 public class PageInfo implements Serializable {
 11     private static final long serialVersionUID = -7404704191471426656L;
 12     // pagesize,每一頁顯示多少
 13     private int showCount = 3;
 14     // 總頁數
 15     private int totalPage;
 16     // 總記錄數
 17     private int totalResult;
 18     // 當前頁數
 19     private int currentPage;
 20     // 當前顯示到的ID,在mysql limit 中就是第一個參數。
 21     private int currentResult;
 22     private String sortField;
 23     private String order;
 24     
 25     // ----------------------------------------------------------------------
 26     
 27     // 分頁結果
 28     private List<?> result;
 29     
 30     // 查詢條件
 31     private List<?> conditions;
 32     
 33     // 開始頁碼
 34     private int start;
 35 
 36     // 每頁多少
 37     private int limit;
 38     
 39     // 成功與否
 40     private boolean success;
 41     
 42     // 總記錄數
 43     private int totalProperty;
 44 
 45     public int getTotalProperty() {
 46         return totalProperty;
 47     }
 48 
 49     public void setTotalProperty(int totalProperty) {
 50         this.totalProperty = totalProperty;
 51     }
 52 
 53     public boolean isSuccess() {
 54         return success;
 55     }
 56 
 57     public void setSuccess(boolean success) {
 58         this.success = success;
 59     }
 60 
 61     public int getStart() {
 62         return start;
 63     }
 64 
 65     public void setStart(int start) {
 66         this.start = start;
 67     }
 68 
 69     public int getLimit() {
 70         return limit;
 71     }
 72 
 73     public void setLimit(int limit) {
 74         this.limit = limit;
 75     }
 76 
 77     public List<?> getConditions() {
 78         return conditions;
 79     }
 80 
 81     public void setConditions(List<?> conditions) {
 82         this.conditions = conditions;
 83     }
 84 
 85     public List<?> getResult() {
 86         return result;
 87     }
 88 
 89     public void setResult(List<?> result) {
 90         this.result = result;
 91     }
 92 
 93     public int getShowCount() {
 94         return showCount;
 95     }
 96 
 97     public void setShowCount(int showCount) {
 98         this.showCount = showCount;
 99     }
100 
101     public int getTotalPage() {
102         return totalPage;
103     }
104 
105     public void setTotalPage(int totalPage) {
106         this.totalPage = totalPage;
107     }
108 
109     public int getTotalResult() {
110         return totalResult;
111     }
112 
113     public void setTotalResult(int totalResult) {
114         this.totalResult = totalResult;
115     }
116 
117     public int getCurrentPage() {
118         return currentPage;
119     }
120 
121     public void setCurrentPage(int currentPage) {
122         this.currentPage = currentPage;
123     }
124 
125     public int getCurrentResult() {
126         return currentResult;
127     }
128 
129     public void setCurrentResult(int currentResult) {
130         this.currentResult = currentResult;
131     }
132 
133     public String getSortField() {
134         return sortField;
135     }
136 
137     public void setSortField(String sortField) {
138         this.sortField = sortField;
139     }
140 
141     public String getOrder() {
142         return order;
143     }
144 
145     public void setOrder(String order) {
146         this.order = order;
147     }
148 }
View Code

4、delegate是一個私有屬性並且沒有提供訪問它的方法,因此我們需要寫一個工具類來訪問它,這個工具類封裝了和它相關的一些方法。

 1 package com.dyl.util;
 2 
 3 import java.lang.reflect.Field;
 4 
 5 /**
 6  * 輔助類
 7  * 
 8  * StatementHandler的默認實現類是RoutingStatementHandler,因此攔截的實際對象是它。
 9  * RoutingStatementHandler的主要功能是分發,它根據配置Statement類型創建真正執行數據庫操作的StatementHandler,並將其保存到delegate屬性里。
10  * 由於delegate是一個私有屬性並且沒有提供訪問它的方法,因此需要借助ReflectHelper的幫忙。
11  * 通過ReflectHelper的封裝后我們可以輕易的獲得想要的屬性。
12  * 
13  * @author dyl
14  * @date 2014-07-12
15  */
16 public class ReflectHelper {
17     public static Field getFieldByFieldName(Object obj, String fieldName) {
18         // 從攔截器的注解中獲取攔截的類名
19         for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
20             try {
21                 return superClass.getDeclaredField(fieldName);
22             } catch (NoSuchFieldException e) {
23             }
24         }
25         return null;
26     }
27 
28     /**
29      * Obj fieldName 獲取屬性值
30      * 
31      * @param obj
32      * @param fieldName
33      * @return
34      * @throws SecurityException
35      * @throws NoSuchFieldException
36      * @throws IllegalArgumentException
37      * @throws IllegalAccessException
38      */
39     public static Object getValueByFieldName(Object obj, String fieldName) throws SecurityException, NoSuchFieldException,
40             IllegalArgumentException, IllegalAccessException {
41         Field field = getFieldByFieldName(obj, fieldName);
42         Object value = null;
43         if (field != null) {
44             if (field.isAccessible()) {
45                 value = field.get(obj);
46             } else {
47                 field.setAccessible(true);
48                 value = field.get(obj);
49                 field.setAccessible(false);
50             }
51         }
52         return value;
53     }
54 
55     /**
56      * Obj fieldName 設置屬性值
57      * 
58      * @param obj
59      * @param fieldName
60      * @param value
61      * @throws SecurityException
62      * @throws NoSuchFieldException
63      * @throws IllegalArgumentException
64      * @throws IllegalAccessException
65      */
66     public static void setValueByFieldName(Object obj, String fieldName, Object value) throws SecurityException, NoSuchFieldException,
67             IllegalArgumentException, IllegalAccessException {
68         Field field = obj.getClass().getDeclaredField(fieldName);
69         if (field.isAccessible()) {
70             field.set(obj, value);
71         } else {
72             field.setAccessible(true);
73             field.set(obj, value);
74             field.setAccessible(false);
75         }
76     }
77 }
View Code

5、休息一下,我們測試一下性能。在插件類中,重寫sql方法,我們運用了StringBuffer、StringBuilder類。根據單線程或多線程的場景使用StringBuilder或StringBuffer。

  1 package com.dyl.test;
  2 
  3 import java.util.ArrayList;
  4 import java.util.Iterator;
  5 import java.util.List;
  6 /**
  7  * String,StringBuffer,StringBuilder的區別。
  8  * String,字符串常量,不可變對象。
  9  * StringBuffer,字符串變量,線程安全。
 10  * StringBuilder,字符串變量,非線程安全。
 11  * 性能:String<StringBuffer<StringBuilder。
 12  * @author Administrator
 13  *
 14  */
 15 public class StringBuilderTest {
 16     private static final String base = " base string. ";
 17     private static final int count = 2000000;
 18 
 19     public static void stringTest() {
 20         long begin, end;
 21         begin = System.currentTimeMillis();
 22         String test = new String(base);
 23         for (int i = 0; i < count / 100; i++) {
 24             test = test + " add ";
 25         }
 26         end = System.currentTimeMillis();
 27         System.out.println((end - begin)
 28                 + " millis has elapsed when used String. ");
 29     }
 30 
 31     public static void stringBufferTest() {
 32         long begin, end;
 33         begin = System.currentTimeMillis();
 34         StringBuffer test = new StringBuffer(base);
 35         for (int i = 0; i < count; i++) {
 36             test = test.append(" add ");
 37         }
 38         end = System.currentTimeMillis();
 39         System.out.println((end - begin)
 40                 + " millis has elapsed when used StringBuffer. ");
 41     }
 42 
 43     public static void stringBuilderTest() {
 44         long begin, end;
 45         begin = System.currentTimeMillis();
 46         StringBuilder test = new StringBuilder(base);
 47         for (int i = 0; i < count; i++) {
 48             test = test.append(" add ");
 49         }
 50         end = System.currentTimeMillis();
 51         System.out.println((end - begin)
 52                 + " millis has elapsed when used StringBuilder. ");
 53     }
 54 
 55     public static String appendItemsToStringBuiler(List list) {
 56         StringBuilder b = new StringBuilder();
 57         for (Iterator i = list.iterator(); i.hasNext();) {
 58             b.append(i.next()).append(" ");
 59         }
 60         return b.toString();
 61     }
 62 
 63     public static void addToStringBuilder() {
 64         List list = new ArrayList();
 65         list.add(" I ");
 66         list.add(" play ");
 67         list.add(" Bourgeois ");
 68         list.add(" guitars ");
 69         list.add(" and ");
 70         list.add(" Huber ");
 71         list.add(" banjos ");
 72         System.out.println(StringBuilderTest.appendItemsToStirngBuffer(list));
 73     }
 74 
 75     public static String appendItemsToStirngBuffer(List list) {
 76         StringBuffer b = new StringBuffer();
 77         for (Iterator i = list.iterator(); i.hasNext();) {
 78             b.append(i.next()).append(" ");
 79         }
 80         return b.toString();
 81     }
 82 
 83     public static void addToStringBuffer() {
 84         List list = new ArrayList();
 85         list.add(" I ");
 86         list.add(" play ");
 87         list.add(" Bourgeois ");
 88         list.add(" guitars ");
 89         list.add(" and ");
 90         list.add(" Huber ");
 91         list.add(" banjos ");
 92         System.out.println(StringBuilderTest.appendItemsToStirngBuffer(list));
 93     }
 94 
 95     public static void main(String[] args) {
 96         stringTest();
 97         stringBufferTest();
 98         stringBuilderTest();
 99         addToStringBuffer();
100         addToStringBuilder();
101     }
102 }
View Code

6、在<configuration>中,配置實現了攔截器的插件。Configuration就像是Mybatis的總管,Mybatis的所有配置信息都存放在這里,此外,它還提供了設置這些配置信息的方法。Configuration可以從配置文件里獲取屬性值,也可以通過程序直接設置。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
 3 <configuration>
 4 
 5 <!-- The content of element type "configuration" must match 
 6  "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,plugins?,
 7  environments?,databaseIdProvider?,mappers?)". 
 8  Mybaits配置文件校驗,節點位置有要求。 -->
 9     
10     <!-- properties里配置的屬性將被存放在Configuration的variables變量里,供Mybatis使用。 -->
11     <properties resource="jdbc.properties"></properties>
12     
13     <!-- 別名是為Java類型命名一個短的名字。它只用在XML配置文件里,用來減少類完全限定名的多余部分。 -->
14     <typeAliases>
15         <typeAlias alias="Company" type="com.dyl.entity.Company"/>
16         <typeAlias alias="Dep" type="com.dyl.entity.Dep"/>
17         <typeAlias alias="Duty" type="com.dyl.entity.Duty"/>
18         <typeAlias alias="Staff" type="com.dyl.entity.Staff"/>
19     </typeAliases>
20     
21     <!-- 自定義攔截模式,配置插件PagePlugin。屬性dialect指示數據庫類型,目前只支持mysql和oracle兩種數據庫。
22     屬性pageSqlId指示攔截的規則,以正則方式匹配。元字符.點,匹配除“\n”之外的任何單個字符。元字符*,匹配前面的子表達式零次或多次(大於等於0次)。 -->
23     <plugins>
24         <plugin interceptor="com.dyl.util.PagePlugin">
25             <property name="dialect" value="oracle"/>
26             <property name="pageSqlId" value=".*ListPage.*"/>
27         </plugin>
28     </plugins>
29     
30     <!-- environments里可以配置多個environment,每個environment對應一個數據庫環境。 -->
31     <environments default="development">
32         <environment id="development">
33             <transactionManager type="JDBC" />
34             <dataSource type="POOLED">
35                 <property name="driver" value="${jdbc.driver}" />
36                 <property name="url" value="${jdbc.url}" />
37                 <property name="username" value="${jdbc.user}" />
38                 <property name="password" value="${jdbc.pwd}" />
39             </dataSource>
40         </environment>
41     </environments>
42     
43     <!-- Mappers用於告訴Mybatis去哪里尋找sql映射文件。 -->
44     <mappers>
45         <mapper resource="com/dyl/entity/xml/Company.xml" />
46         
47     </mappers>
48 
49 </configuration>
View Code

7、Company類和數據庫表company一一對應。在Company的映射文件Company.xml中增加分頁查詢測試。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
 3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 4 
 5 <mapper namespace="com.dyl.dao.ICompanyDao">
 6 
 7     <select id="selectCompanyById" parameterType="BigDecimal" resultType="Company">
 8         select * from company where companyid = #{id}
 9     </select>
10 
11     <!-- 為了返回list 類型而定義的returnMap -->
12     <resultMap type="Company" id="resultListCompany">
13         <id column="companyId" property="companyId" />
14         <result column="companyName" property="companyName" />
15         <result column="address" property="address" />
16         <result column="telephone" property="telephone" />
17         <result column="leader" property="leader" />
18         <result column="mobilePhone" property="mobilePhone" />
19         <result column="remark" property="remark" />
20     </resultMap>
21 
22     <!-- 返回list 的select 語句,注意 resultMap 的值是指向前面定義好的 -->
23     <select id="selectCompanys" parameterType="string" resultMap="resultListCompany">
24         select * from company where companyName like #{companyName} order by companyid
25     </select>
26 
27     <!--執行增加操作的SQL語句。id和parameterType分別與ICompanyDao接口中的addCompany方法的名字和參數類型一致。以#{name}的形式引用Company參數 
28         的name屬性,MyBatis將使用反射讀取Company參數的此屬性。#{name}中name大小寫敏感。引用其他的gender等屬性與此一致。useGeneratedKeys設置 
29         為"true",表明要MyBatis獲取由數據庫自動生成的主鍵;keyProperty="companyid"指定把獲取到的主鍵值注入到Company的companyid屬性 -->
30     <insert id="addCompany" parameterType="Company" useGeneratedKeys="true" keyProperty="companyid">
31         insert into company(companyName,address,telephone,leader,mobilePhone,remark) values
32         (#{companyName},#{address},#{telephone},#{leader},#{mobilePhone},#{remark})
33     </insert>
34 
35     <update id="updateCompany" parameterType="Company">
36         update company set companyName=#{companyName},address=#{address},telephone=#{telephone},
37         leader=#{leader},mobilePhone=#{mobilePhone},remark=#{remark} where companyId=#{companyId}
38     </update>
39 
40     <delete id="deleteCompany" parameterType="BigDecimal">
41         delete from company where companyid=#{id}
42     </delete>
43     
44     <!-- 分頁查詢測試 -->    
45     <select id="selectCompanyListPage" resultMap="resultListCompany">
46         select * from company
47     </select>
48     
49 </mapper>
View Code

8、在Company.xml的<mapper namespace="com.dyl.dao.ICompanyDao">dao接口中增加分頁查詢測試。映射文件Company.xml和接口ICompanyDao,三個一致,接口名字一致,方法名字一致,方法參數類型一致。

 1 package com.dyl.dao;
 2 
 3 import java.math.BigDecimal;
 4 import java.util.List;
 5 
 6 import com.dyl.entity.Company;
 7 import com.dyl.util.PageInfo;
 8 
 9 /**
10  * dao接口
11  * @author dyl
12  * @date 2014-7-13
13  */
14 public interface ICompanyDao {
15     /**
16      * 根據分公司id查找分公司
17      * 
18      * @param id
19      * @return
20      */
21     public Company selectCompanyById(BigDecimal id);
22 
23     /**
24      * 查找分公司
25      * 
26      * @param companyName
27      * @return
28      */
29     public List<Company> selectCompanys(String companyName);
30 
31     /**
32      * 增加分公司
33      * 
34      * @param company
35      */
36     public void addCompany(Company company);
37 
38     /**
39      * 修改分公司
40      * 
41      * @param company
42      */
43     public void updateCompany(Company company);
44 
45     /**
46      * 刪除分公司
47      * 
48      * @param id
49      */
50     public void deleteCompany(BigDecimal id);
51 
52     /**
53      * 分頁查詢測試
54      * 
55      * @param page
56      * @return
57      */
58     public List<Company> selectCompanyListPage(PageInfo page);
59     
60     /**
61      * 分頁查找
62      * @param page 條件
63      * @return
64      */
65     public List<?> findByPage(PageInfo page);
66 
67     /**
68      * 分頁查找的總記錄
69      * @param page 條件
70      * @return
71      */
72     public int findByCount(PageInfo page);
73     
74     /**
75      * 修改公司信息
76      * @param company
77      * @return
78      * @throws Exception 
79      */
80     public Integer update(Company company) throws Exception;
81 }
View Code

9、測試。在測試前,我們運用Classloader 類加載器美化一下項目工程結構,在Java Resources在新建config,與src並行,把可以用Java類加載的配置文件移動到config。測試類中,有增加公司測試,可以看到Company類的屬性,companyId是oracle主鍵自增長的(前面章節完成)。

  1 package com.dyl.test;
  2 
  3 import java.io.InputStream;
  4 import java.io.Reader;
  5 import java.math.BigDecimal;
  6 import java.util.List;
  7 
  8 import org.apache.ibatis.io.Resources;
  9 import org.apache.ibatis.session.SqlSession;
 10 import org.apache.ibatis.session.SqlSessionFactory;
 11 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 12 
 13 import com.dyl.dao.ICompanyDao;
 14 import com.dyl.entity.Company;
 15 import com.dyl.util.PageInfo;
 16 /**
 17  * 測試類
 18  * @author dyl
 19  * @date 2014-7-12
 20  */
 21 public class CompanyXmlTest {
 22     private static SqlSessionFactory sqlSessionFactory;
 23     private static Reader reader;
 24     // private static InputStream reader;
 25     
 26     /**
 27      * Classloader 類加載器,用來加載 Java 類到 Java 虛擬機中。
 28      * 
 29      * (1)Class.getResourceAsStream(String path) : path不以’/'開頭時默認是從此類所在的包下取資源,以’/'開頭則是從ClassPath根下獲取。
 30      * 其只是通過path構造一個絕對路徑,最終還是由ClassLoader獲取資源。
 31      * 
 32      * (2)Class.getClassLoader.getResourceAsStream(String path):默認則是從ClassPath根下獲取,path不能以’/'開頭, 
 33      * 最終是由ClassLoader獲取資源。 
 34      * 
 35      * (3)Resources 類加載資源。
 36      * 對於簡單的只讀文本數據,加載為Reader。
 37      * 對於簡單的只讀二進制或文本數據,加載為 Stream。 
 38      * 對於可讀寫的二進制或文本文件,加載為 File。
 39      * 對於只讀的配置屬性文件,加載為 Properties。 
 40      * 對於只讀的通用資源,加載為 URL。 
 41      * 按以上的順序,Resources類加載資源的方法如下: 
 42      * Reader getResourceAsReader(String resource); 
 43      * Stream getResourceAsStream(String resource); 
 44      * File getResourceAsFile(String resource); 
 45      * Properties getResourceAsProperties(String resource); 
 46      * Url getResourceAsUrl(String resource);
 47      * 默認則是從ClassPath根下獲取,resource不能以’/'開頭,最終是由ClassLoader獲取資源。 
 48      */
 49     
 50     static {
 51         try {
 52             reader = Resources.getResourceAsReader("sqlMap-config.xml");
 53             // reader = Resources.class.getResourceAsStream("/sqlMap-config.xml");
 54             // reader = Resources.class.getClassLoader().getResourceAsStream("sqlMap-config.xml");
 55             sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
 56         } catch (Exception e) {
 57             e.printStackTrace();
 58         }
 59     }
 60 
 61     public static SqlSessionFactory getSession() {
 62         return sqlSessionFactory;
 63     }
 64     
 65     /**
 66      * 查找分公司
 67      * @param companyName
 68      */
 69     public void getCompanyList(String companyName){
 70         SqlSession session = sqlSessionFactory.openSession();
 71         try {
 72             ICompanyDao companyDao=session.getMapper(ICompanyDao.class);           
 73             List<Company> companys = companyDao.selectCompanys(companyName);
 74             for(Company company:companys){
 75                 System.out.println(company.getCompanyId().toString()+","+company.getCompanyName()+","
 76                         +company.getAddress());
 77             }  
 78         } finally {
 79             session.close();
 80         }
 81     }
 82     
 83     /**
 84      * 測試增加,增加后,必須提交事務,否則不會寫入到數據庫。
 85      */
 86     public void addCompany(){       
 87         Company company=new Company();
 88         company.setCompanyName("妖靈科技");
 89         company.setAddress("四川成都");
 90         company.setTelephone("028-88888888");
 91         company.setLeader("妖靈");
 92         company.setMobilePhone("18888888888");
 93         company.setRemark("HO");     
 94         SqlSession session = sqlSessionFactory.openSession();
 95         try {
 96             ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
 97             companyDao.addCompany(company);
 98             session.commit();
 99         } finally {
100             session.close();
101         }
102     }
103     /**
104      * 先得到公司,然后修改,提交。
105      */
106     public void updateCompany(){
107         SqlSession session = sqlSessionFactory.openSession();
108         try {            
109             ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
110             Company company=companyDao.selectCompanyById(new BigDecimal(2));
111             company.setAddress("北京");
112             companyDao.updateCompany(company);
113             session.commit(); 
114         } finally {
115             session.close();
116         }
117     }
118     
119     /**
120      * 刪除數據,刪除一定要commit。
121      * @param id
122      */
123     public void deleteCompany(BigDecimal id){
124         SqlSession session = sqlSessionFactory.openSession();
125         try {            
126             ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
127             companyDao.deleteCompany(id);
128             session.commit();            
129         } finally {
130             session.close();
131         }
132     }
133     
134     public static void main(String[] args) {
135         SqlSession session = sqlSessionFactory.openSession();
136         try {
137             ICompanyDao companyDao=session.getMapper(ICompanyDao.class);
138             
139 //            // 根據分公司id查找分公司
140 //            Company company=companyDao.selectCompanyById(new BigDecimal(5));
141 //            System.out.println(company.getCompanyName());
142 //            System.out.println(company.getAddress());
143 //            System.out.println();
144             
145 //            // 查找分公司
146 //            CompanyXmlTest companyTest=new CompanyXmlTest();
147 //            companyTest.getCompanyList("%");
148 //            System.out.println();
149             
150 //            // 增加分公司
151 //            Company company=new Company();
152 //            company.setCompanyName("海口分公司");
153 //            company.setAddress("海南海口");
154 //            company.setTelephone("0898-88888888");
155 //            company.setLeader("碧波");
156 //            company.setMobilePhone("18888888888");
157 //            company.setRemark("東方夏威夷");     
158 //            companyDao.addCompany(company);
159 //            session.commit();
160 //            System.out.println("當前增加的公司名稱為:"+company.getCompanyName());
161             
162 //            // 修改分公司
163 //            Company company=companyDao.selectCompanyById(new BigDecimal(4));
164 //            company.setAddress("天津");
165 //            company.setCompanyName("天津分公司");
166 //            company.setLeader("聞其");
167 //            company.setMobilePhone("18888888888");
168 //            company.setRemark("首都門戶");
169 //            company.setTelephone("022-88888888");
170 //            companyDao.updateCompany(company);
171 //            session.commit();
172 //            System.out.println("修改成功");
173             
174 //            // 刪除分公司
175 //            companyDao.deleteCompany(new BigDecimal(5));
176 //            session.commit();
177 //            System.out.println("刪除成功");
178             
179             // 分頁查詢測試
180             PageInfo page=new PageInfo();
181             page.setShowCount(5);// 每一頁顯示多少
182             //page.setCurrentResult(5);// 當前顯示到的id
183             page.setCurrentPage(2);// 當前頁數
184             List<Company>companys=companyDao.selectCompanyListPage(page);
185             for(Company company:companys){
186                 System.out.println(company.getCompanyId().toString()+","+company.getCompanyName()+","
187                         +company.getAddress());
188             }
189                     
190         } finally {
191             session.close();
192         }
193     }
194 }
View Code

10、項目工程圖。

     接下來,(1)我們需要根據實際開發需求選擇使用struts2或者springmvc來實現控制層,選擇前需要大概了解一下她們的差異,她們各自擅長什么。(2)確定框架組合后,碰到一些常用功能就封裝成jar包,方便復用。(3)適當暫停,整理整理,測試測試,備份備份,休息休息,思考思考。很暈很暈。一定要勤於備份,一定要備份。OK,我們下次見。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM