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