现在的位置: 首页 > 综合 > 正文

购买咨询项目(2)

2018年04月10日 ⁄ 综合 ⁄ 共 9567字 ⁄ 字号 评论关闭

项目总结专栏地址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

大家觉得呢?

----------------------------------------------------------------------------------------------------------------------------------

下期: 
购买咨询3 AbstractJUnit4SpringContextTests EasyMock

抱歉!评论已关闭.