• 个人知识管理系统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指示拦截的规则,以正则方式匹配。元字符.点,匹配除“
    ”之外的任何单个字符。元字符*,匹配前面的子表达式零次或多次(大于等于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,我们下次见。

  • 相关阅读:
    [洛谷P4585] [FJOI2015] 火星商店问题
    [bzoj4311] 向量
    [bzoj4977] [Lydsy1708月赛] 跳伞求生
    sdut-1153 C语言实验——求两个整数之中较大者
    sdut_1116
    sdut_1189
    汉诺塔
    二分查找
    类似二分查找算法
    [YTU]_2922(Shape系列-8)
  • 原文地址:https://www.cnblogs.com/youla/p/3841998.html
Copyright © 2020-2023  润新知