项目总结专栏地址:http://blog.csdn.net/column/details/project-summary.html
故事发生在2011.10-12时间段。
上期回顾:购买咨询1 Calendar velocityCount讲解
分享了2点,还有以下需要备忘
3)商品分类的三级联动处理技巧
4)Select页面标签的变种,很好用。
5)Ajax处理局部页面(改为光标定位)
6)数据库的主从配置
7)Ibatis的sqlmap配置的一些处理技巧
8)AbstractJUnit4SpringContextTests对数据库测试的使用
9)EasyMock在单元测试的使用
这次可能先分享3-6点的知识,感觉东西太多。写下来太长了。
--------------------------------分割一下---------------------
那我们首先讲第三点:三级联动
VM结构:
<td width="86" align="right">商品分类:</td> <td width="350"> <select class="inp_1" id="top" name="consultationCondition.level1Id"onchange="searchSec(this.value);"> #if($!topSorts) <option value="" #if(!$!consultationCondition.level1Id) selected="selected" #end>选择一级分类</option> #foreach($sort in $topSorts) <option value="$!sort.id" #if($!sort.id == $!consultationCondition.level1Id) selected="selected" #end >$!sort.name</option> #end #end </select> <select class="inp_1" id="sec" name="consultationCondition.level2Id" onchange="searchThi(this.value);"> #if($!secSortList) <option value="" #if(!$!consultationCondition.level2Id) selected="selected" #end>选择二级分类</option> #foreach($sort in $secSortList) <option value="$!sort.id" #if($!sort.id == $!consultationCondition.level2Id) selected="selected" #end >$!sort.name</option> #end #end </select> <select class="inp_1" name="consultationCondition.level3Id" id="thi"> #if($!thirdSorts) <option value="" #if(!$!consultationCondition.level3Id) selected="selected" #end>选择三级分类</option> #foreach($sort in $thirdSorts) <option value="$!sort.id" #if($!sort.id == $!consultationCondition.level3Id) selected="selected" #end >$!sort.name</option> #end #end </select> </td>
页面这里,需要给大家做一下说明:
topSorts变量是进入此页面,就必须初始好了的。searchSec()方法当一级分类改变时,去查询二级分类;searchThi()当二级分类改变时,去查询三级分类。现在整个逻辑应该好理解了。
JS调用代码如下
function searchSec(parentId){ $('#thi').empty(); $('#sec').append($("<option value=''>"+"请选择"+"</option>")); if(parentId == ""){ $('#sec').append($("<option value=''>"+"请选择"+"</option>")); $('#sec').empty(); return; } jQuery.ajax({ type : "post", url : "$!homeModule.getTarget("/relation/getSortByParent.action")", data : "parentId="+parentId, dataType : "json", //返回json数据 success: getSec, error : function(data){ alert("查询失败"); } }); } function getSec(data){ var pathArry = eval(data); $('#sec').empty(); $('#sec').append($("<option value=''>"+"请选择"+"</option>")); for(var i = 0,len = pathArry.length; i<len; i++){ $('#sec').append($("<option value='"+pathArry[i].id+"'>"+pathArry[i].name+"</option>")); } } function searchThi(parentId){ if(parentId == ""){ $('#thi').empty(); $('#thi').append($("<option value=''>"+"请选择"+"</option>")); return; } jQuery.ajax({ type : "post", url : "$!homeModule.getTarget("/relation/getSortByParent.action")", data : "parentId="+parentId, dataType : "json", //返回json数据 success: getThi, error : function(parentId){ alert("查询失败"); } }); } function getThi(data){ var pathArry = eval(data); $('#thi').empty(); $('#thi').append($("<option value=''>"+"请选择"+"</option>")); for(var i = 0,len = pathArry.length; i<len; i++){ $('#thi').append($("<option value='"+pathArry[i].id+"'>"+pathArry[i].name+"</option>")); } }
这获取二级与三级分类的JS方法,基本思路都是一样的,那主要针对第二级分类进行详解一下:
首先需要将第一级分类的Id作为参数传进来,在获取第二级分类之前,先做一些前期的准备,将第三级分类置为空,将第二级分类设置为请选择,还有判断一下参数是否为空,当这些都满足的时候,再做post请求第二级分类数据。
后台也校验通过之后,将返回数据,ajax接受,success后再调用动态组装html代码的逻辑函数,也就是上面的getSec();值得注意的是:请求后服务器响应的数据格式是JsonArray的,这个用了eval(data)方式处理了一下。
那现在来看一下后端的代码是如何处理的:
public void getSortByParent(){ response.setCharacterEncoding("utf-8"); List<Map> chilidMapList = new ArrayList<Map>(); if (parentId != null) { List<ProductSort> sorts = sortsService.getSortsByParent(parentId); for(ProductSort temp : sorts){ Map<String, String> sortMap = new HashMap<String, String>(); sortMap.put("id", String.valueOf(temp.getId())); sortMap.put("name", temp.getName()); chilidMapList.add(sortMap); } } JSONArray json = JSONArray.fromObject(chilidMapList); try { response.getWriter().write(json.toString()); } catch (IOException e) { } }
从这里能看出,表结构如key-value一样,再多一个字段,parentID,就成了。首先,对响应的数据做了一个chartset的设置,数据结构:list里存map,这都好理解。这些分类都是key-value构成的,这后将数据转换成一个Json数组,用Writer写回。
一遍浏览下来,所有的思路,流程还是挺清晰的,三级联动,其实是一个很常见的功能模块,留这儿备用,也可作为一种思路,参考。
---------------------------进行分割---------------------------------------
Every man is the master of his own fortune
每个人都是他自己的命运的主宰。
---------------------------------------------回来继续--------------------------
4) Select页面标签的变种
页面构成如下:
<td align="right">商品采销员:</td> <td> <div id="storeDiv1" style="width:40px;height: 30px;"></div> </td> <input type="hidden" id="addFormStoreId" name="consultationCondition.cgid" value="$!consultationCondition.cgid"/> <textarea id="adminId" style="display:none">$!admins</textarea>
当然还需要js的配合,看下面:
<script> var admin = document.getElementById("adminId").value; var value = document.getElementById("addFormStoreId").value; var stores1 = new JSelect(admin,"id","name"); stores1.setPageSize(15); stores1.setSelectValue(value); stores1.render("storeDiv1","addFormStoreId"); </script>
当然这只是一部分基本的处理,还需要引入一个js插件,需要的话,在发给大家吧,毕竟个自己做的备忘。
两个隐藏标签,标签textarea标签里包含了所有的采销员的key-value信息,将输入的信息,查看到后,在渲染到div中去,有点google搜索推荐那种味道,看起了很爽的。当然了,admins变量是初始页面就必须要有才信息。
public String getAdmins() { response.setCharacterEncoding("utf-8"); List<Map> chilidMapList = new ArrayList<Map>(); List<Admin> admins = getAllAdmins(); for (Admin temp : admins) { Map<String, String> sortMap = new HashMap<String, String>(); sortMap.put("id", String.valueOf(temp.getId())); sortMap.put("name", temp.getOpName()); chilidMapList.add(sortMap); } JSONArray json = JSONArray.fromObject(chilidMapList); return json.toString(); }
可以看到这也是json的数据格式,用的ognl表达式,符号Strust2规范的,有get方法,页面就可以拿到这个变量。
一个东西,又告一段落了,下面,猜到了,分割休闲
------------------------------------------------分割线-----------------------------------
5)Ajax处理局部页面
这一点就略过吧,三级联动其实就用到了ajax了的。这个给补一个定位光标的吧:
<textarea id="t_sword" name="consultation.ureply" cols="60" rows="6">您好!感谢您XXXXX</textarea>
需求就是:每当打开的时候,光标定位到您好!之后,感谢之前。
直接给js源码吧,大家拿回去自己琢磨吧。
var isIE = !(!document.all); function moveCursor(){ var oTextarea = document.getElementById("t_sword"); var start = 3; var end = 3; if(isNaN(start)||isNaN(end)){ alert("位置输入错误"); } if(isIE){ var oTextRange = oTextarea.createTextRange(); var LStart = start; var LEnd = end; var start = 0; var end = 0; var value = oTextarea.value; for(var i=0; i<value.length && i<LStart; i++){ var c = value.charAt(i); if(c!='\n'){ start++; } } for(var i=value.length-1; i>=LEnd && i>=0; i--){ var c = value.charAt(i); if(c!='\n'){ end++; } } oTextRange.moveStart('character', start); oTextRange.moveEnd('character', -end); //oTextRange.collapse(true); oTextRange.select(); oTextarea.focus(); }else{ oTextarea.select(); oTextarea.selectionStart=start; oTextarea.selectionEnd=end; } }
---------------我是来分割的--------------------------------------马上回来-------
现在来点不是页面层的东西给大家。
6)数据库的主从配置
先得有一个java类,做动态处理
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 动态数据源. */ public class DynamicDataSource extends AbstractRoutingDataSource { private void setCustomDataSources(Map customDataSources) { } @Override public void setTargetDataSources(Map targetDataSources) { super.setTargetDataSources(targetDataSources); // 设置读库数量 DataSourceSwitcher.READ_COUNT = targetDataSources.size(); } @Override protected Object determineCurrentLookupKey() { return DataSourceSwitcher.getDataSource(); } }
以及业务逻辑的数据库处理DAO的实现的Java代码:
public class ConsultationDaoImpl extends BaseDao implements ConsultationDao { @Override public int getAllConsCount(ConsSearchParameter consSearchParameter) { //随即读取 DataSourceSwitcher.setEitherSlave(); return (Integer) queryForObject("Cons.getAllConsCount", consSearchParameter); } }
有了这个,我们再来看看,Spring里面是怎么配置的,这个也简单啦。
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="classpath:sqlmap-config.xml" /> </bean> <!-- 购买问题咨询dao --> <bean id="consultationDao" class="com.pro.dao.consultation.impl.ConsultationDaoImpl"> <property name="dataSource" ref="dynamicProDataSource" /> </bean> <bean id="dynamicProDataSource" class="com.project.common.dao.dynamic.DynamicDataSource"> <property name="targetDataSources"> <map key-type="java.lang.String"> <!-- 约定俗成,主从库名为slave1,其它从库依次23...n --> <entry key="slave1" value-ref="proR1DataSource" /> </map> </property> <property name="defaultTargetDataSource" ref="proWriterDataSource" /> </bean>
从这里可以看出,配置了一个动态的数据源,这里会根据key值来取对应的数据源;以及还配置了一个默认的数据源。
那这里我们来具体看一些proR1DataSource这个数据源的具体配置情况。
<bean id="parentDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="maxWait" value="30000"></property> <property name="validationQuery" value="SELECT 1"></property> <property name="testOnBorrow" value="false"></property> <property name="testWhileIdle" value="true"></property> <property name="timeBetweenEvictionRunsMillis"> <value>30000</value> </property> </bean> <bean id="proR1DataSource" parent="parentDataSource"> <property name="driverClassName" value="${pro.r1.jdbc.driver}" /> <property name="url" value="${pro.r1.jdbc.url}" /> <property name="username" value="${pro.r1.jdbc.username}" /> <property name="password" value="${pro.r1.jdbc.password}" /> </bean> pro.r1.jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver pro.r1.jdbc.url=jdbc:sqlserver://10.101.24.125:1433;databaseName=pro;SendStringParametersAsUnicode=false pro.r1.jdbc.username=readPro pro.r1.jdbc.password=0plm5
首先有一个parentDataSource来做一些基础的有必须的配置信息,也可以叫公共的一些配置信息吧,然后配置proR1DataSource的链接详情,这里的properties是随便乱写的,以供参考而已。properties里面的变量是配置在Maven的pom文件,通过打包的时候,动态组装Spring的配置文件里去的。这种方式也很好的实现了不用重启而切换数据库吧。
差不多就这些吧,做了个备忘,下期还有3个点需要巩固分享,这期的这些东西还是挺easy的。
--------------------------我来了------分割-----------------------------------------
说说前几天帮朋友看的出的一个配置,突然感觉,好便宜。给大家show一下,不过这个不是游戏型的机器,只适合家庭办公而已。
CPUAMD 型号:AMD Athlon II X4 740 ¥459
主板 映泰 型号:Hi-Fi A75S3 ¥399
显卡 昂达 型号:G210 1024MB SD3 ¥199
显示器 明基 型号:G2250(21.5英寸) ¥666
硬盘 西部数据 型号:WD5000AAKX(500GB)¥339
机箱 爱国者 型号:CA-E620A ¥99
电源 昂达 型号:昂达走线大师红魔 ¥129
内存 威刚 型号:万紫千红 DDR3 1600 2G 台式机内存 ¥129
光驱 三星 型号:SH-118BB ¥81
键鼠套装 罗技 型号:MK120 ¥79
音响 屁颠虫 型号:010A加强版 ¥29
鼠标垫 讯拓 型号:幽灵蜂GP200( 255*212*2MM)¥8
总¥2616
大家觉得呢?
----------------------------------------------------------------------------------------------------------------------------------