首页 > 代码库 > 个人知识管理系统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 }
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 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 }
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 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>
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 }
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 }
10、项目工程图。
接下来,(1)我们需要根据实际开发需求选择使用struts2或者springmvc来实现控制层,选择前需要大概了解一下她们的差异,她们各自擅长什么。(2)确定框架组合后,碰到一些常用功能就封装成jar包,方便复用。(3)适当暂停,整理整理,测试测试,备份备份,休息休息,思考思考。很晕很晕。一定要勤于备份,一定要备份。OK,我们下次见。