首页 > 代码库 > 个人知识管理系统Version1.0开发记录(10)

个人知识管理系统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                     // 重写sql127                     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 ps139      * @param mappedStatement140      * @param boundSql141      * @param parameterObject142      * @throws SQLException143      */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 = http://www.mamicode.com/null;160                     } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {161                         value =http://www.mamicode.com/ parameterObject;162                     } else if (boundSql.hasAdditionalParameter(propertyName)) {163                         value =http://www.mamicode.com/ boundSql.getAdditionalParameter(propertyName);164                     } else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX) 165                             && boundSql.hasAdditionalParameter(prop.getName())) {166                         value =http://www.mamicode.com/ boundSql.getAdditionalParameter(prop.getName());167                         if (value != null) {168                             value =http://www.mamicode.com/ configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));169                         }170                     } else {171                         value = http://www.mamicode.com/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 sql186      * @param page187      * @return188      */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 sql216      * @param page217      * @return218      */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 sql229      * @param page230      * @return231      */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 dyl14  * @date 2014-07-1215  */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 obj32      * @param fieldName33      * @return34      * @throws SecurityException35      * @throws NoSuchFieldException36      * @throws IllegalArgumentException37      * @throws IllegalAccessException38      */39     public static Object getValueByFieldName(Object obj, String fieldName) throws SecurityException, NoSuchFieldException,40             IllegalArgumentException, IllegalAccessException {41         Field field = getFieldByFieldName(obj, fieldName);42         Object value = http://www.mamicode.com/null;43         if (field != null) {44             if (field.isAccessible()) {45                 value =http://www.mamicode.com/ field.get(obj);46             } else {47                 field.setAccessible(true);48                 value =http://www.mamicode.com/ field.get(obj);49                 field.setAccessible(false);50             }51         }52         return value;53     }54 55     /**56      * Obj fieldName 设置属性值57      * 58      * @param obj59      * @param fieldName60      * @param value61      * @throws SecurityException62      * @throws NoSuchFieldException63      * @throws IllegalArgumentException64      * @throws IllegalAccessException65      */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 companyid25     </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) values32         (#{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 company47     </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 dyl12  * @date 2014-7-1313  */14 public interface ICompanyDao {15     /**16      * 根据分公司id查找分公司17      * 18      * @param id19      * @return20      */21     public Company selectCompanyById(BigDecimal id);22 23     /**24      * 查找分公司25      * 26      * @param companyName27      * @return28      */29     public List<Company> selectCompanys(String companyName);30 31     /**32      * 增加分公司33      * 34      * @param company35      */36     public void addCompany(Company company);37 38     /**39      * 修改分公司40      * 41      * @param company42      */43     public void updateCompany(Company company);44 45     /**46      * 删除分公司47      * 48      * @param id49      */50     public void deleteCompany(BigDecimal id);51 52     /**53      * 分页查询测试54      * 55      * @param page56      * @return57      */58     public List<Company> selectCompanyListPage(PageInfo page);59     60     /**61      * 分页查找62      * @param page 条件63      * @return64      */65     public List<?> findByPage(PageInfo page);66 67     /**68      * 分页查找的总记录69      * @param page 条件70      * @return71      */72     public int findByCount(PageInfo page);73     74     /**75      * 修改公司信息76      * @param company77      * @return78      * @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 id122      */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);// 当前显示到的id183             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,我们下次见。