`
doris1bruce
  • 浏览: 4371 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论
收藏列表
标题 标签 来源
highcharts的封装与使用 highcharts http://blog.csdn.net/mylovedeye/article/details/8202015?utm_source=tuicool
使用环境:struts2 json

使用目的:统计图

流程:只需要在jsp页面配置,就能实现数据的线状图,柱状图,饼状图的统计

一个例子:

1.JSP页面:
[html] view plaincopy

    <%@ page contentType="text/html; charset=utf-8" %>  
    <%@ taglib prefix="s" uri="/struts-tags"%>  
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
    <html xmlns="http://www.w3.org/1999/xhtml">  
    <head>  
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />  
    <title>浙江省食品药品监督管理局公众服务平台</title>  
    <link href="/css/main.css" rel="stylesheet" type="text/css" />  
    <link href="/css/column.css" rel="stylesheet" type="text/css" />  
    <link href="/css/myChart.css" rel="stylesheet" type="text/css" />  
    <script language="javascript" type="text/javascript" src="/js/jquery-1.5.2.min.js" ></script>  
    <script language="javascript" type="text/javascript" src="/jqPlugIn/highcharts/highcharts.js"></script>  
    <script language="javascript" type="text/javascript" src="/js/myChart.js" ></script>  
    <script language="javascript" type="text/javascript" src="/js/myChartTheme.js" ></script>  
    <script language="javascript" type="text/javascript" src="/My97DatePicker/WdatePicker.js"></script>  
    <script type="text/javascript">  
    $(function(){  
        var sql = "select count(*) from tb_interac_consultative where time_treatment between ? and ? ";  
          
        // 初始化线状图对象  
        var line = new MyChart(0);  
        line.xAppend("已回复",sql+"and ct_treatment_status=?",new Array("1"));  
        line.xAppend("回复中",sql+"and ct_treatment_status=?",new Array("2"));  
        line.xAppend("未回复",sql+"and ct_treatment_status=?",new Array("0"));  
        line.setTime("timeStart","timeEnd","timetype");  
              
        // 初始化柱状图对象  
        var column = new MyChart(1).cloneAttr(line);  
          
        // 初始化饼状图对象  
        var pie = new MyChart(2);  
        pie.fAppend("已回复",sql+"and ct_treatment_status=?",new Array("1"));  
        pie.fAppend("回复中",sql+"and ct_treatment_status=?",new Array("2"));  
        pie.fAppend("未回复",sql+"and ct_treatment_status=?",new Array("0"));  
        pie.setTime("timeStart","timeEnd","timetype");  
          
        var myHighcharts = new MyHighcharts({  
            title:"科大公司在线咨询信息统计",  
            subTitle:"Source:http://www.zjda.com",  
            xTitle:"人数",  
            yTitle:"信息条数",  
            line:line,  
            column:column,  
            pie:pie  
        });  
          
        myHighcharts.draw(0);     
    });  
    </script>  
      
    </head>  
    <body>  
    <jsp:include page="/header.jsp" flush="true"/>  
    <div class="tenHeight"></div>  
    <table width="960" border="0" align="center" cellpadding="0" cellspacing="0">  
      <tr>  
        <td width="706" align="left" valign="top"><table width="100%" border="0" cellpadding="0" cellspacing="0" class="positionArea">  
          <tr>  
            <td>当前位置:<a href="/">首页</a>>> <a href="/onlineInteract.jsp">互动信息</a>>> <a href="/zjfda/statistic/consultative.jsp">咨询信息统计</a></td>  
          </tr>  
        </table>  
    <div class="tenHeight"></div>  
      
    <!-- 数据统计图 -->  
    <div id="myChart">  
      
        <!-- 导航 -->  
        <ul id="navigation">  
          <li><a href="#" class="current">线状图</a></li>  
          <li><a href="#">柱状图</a></li>  
          <li><a href="#">饼状图</a></li>  
        </ul>  
          
        <!-- 统计图 -->  
        <div id="container"></div>  
          
        <!-- 如果没有用到时间动态统计则删除 -->  
        <div id="timeselect">  
            时间类型:  
            <select id="timetype">  
                <!-- <option value="3">年份</option> -->  
                <option value="2" selected="selected">月份</option>  
                <!-- <option value="1">天数</option> -->  
            </select>  
            时间段:<input type="text" class="Wdate" id="timeStart" size="12" onclick="WdatePicker()" value="2012-01-01"/>  
                        至<input type="text" class="Wdate" id="timeEnd" size="12" onclick="WdatePicker()" value="2012-12-30"/>  
        </div>  
    </div>  
    <!-- 数据统计图 -->  
      
    <div class="tenHeight"></div>  
    <jsp:include page="/footer.jsp" flush="true"/>  
    </body>  
    </html>  

2.myChart.js

[javascript] view plaincopy

    // 封装请求参数对象  
    var Emtoy = function(f,name,sql,params){  
        this.f = -1;  
        this.name = name;  
        this.sql = sql;  
        if(params!=null){  
            this.params = params.concat();  
        }else{  
            this.params = null  
        }  
    }  
      
    /** 
     * 统计图对象对应的JAVA类MyChart.java 
     * @param : typechart  
     *          0表示线状图对象  
     *          1表示柱状图对象  
     *          2表示单饼状图对象  
     *          3表示内嵌饼图对象 
     */  
    var MyChart = function(typechart){  
          
        this.title;//统计图标题  
        this.subtitle;//统计图副标题  
        this.xTitle;//x轴标题  
        this.yTitle;//主轴标题  
          
        this.typedb;//服务器端的数据源  
        this.typechart = typechart;//统计图类类型  
        this.typetime = 0;//统计的时间类型  
        this.emtoys = new Array();//需要统计的参数  
        this.smtoys = new Array();//需要统计的参数,当统计图是内嵌饼图的时候用到  
        this.categories = new Array();//发送到服务器时间分段  
        this.categoriesLocal = new Array();//本地轴分段名称  
      
        this.timeAry = new Array();//保存从页面取得的时间的ID  
        /** 
         * x轴统计内容 
         * @param : name   系列名称 
         * @param : sql    查询语句 
         * @param : params 查询参数 
         */  
        this.xAppend = function(name,sql,params){  
            this.emtoys.push(new Emtoy(null,name,sql,params));  
        }  
        /** 
         * y轴系列内容,时间段查询用 
         * @param : name   时间段名称 
         * @param : time   时间 
         */  
        this.yAppend = function(name,time){  
            this.categories.push(time);  
            this.categoriesLocal.push(name);  
        }  
        /** 
         * 饼状图外层 
         * @param : name   系列名称 
         * @param : sql    查询语句 
         * @param : params 查询参数 
         */  
        this.fAppend = function(name,sql,params){  
            this.emtoys.push(new Emtoy(null,name,sql,params));  
        }  
        /** 
         * 饼状图内层 
         * @param : f      外层饼状图的标志 
         * @param : name   系列名称 
         * @param : sql    查询语句 
         * @param : params 查询参数 
         */  
        this.sAppend = function(f,name,sql,params){  
            this.smtoys.push(new Emtoy(f,name,sql,params));  
        }  
        /** 
         * 保存y轴系列时间段,从页面读取 
         * @param : timeStart   页面开始时间的ID 
         * @param : timeEnd     页面结束时间的ID 
         * @param : timetype    页面时间的类型,年或月或日 
         */  
        this.setTime = function(timeStart,timeEnd,timetype){  
            this.timeAry.push(timeStart);  
            this.timeAry.push(timeEnd);  
            this.timeAry.push(timetype);  
        }  
        /** 
         * 设置y轴系列时间段,从页面读取 
         * @param : timeStart   页面开始时间的ID 
         * @param : timeEnd     页面结束时间的ID 
         * @param : timetype    页面时间的类型,年或月或日 
         */  
        this.getPageTime = function(){  
            if(this.timeAry.length!=0){           
                this.categories = new Array();  
                this.categories.push($("#"+this.timeAry[0]).val());  
                this.categories.push($("#"+this.timeAry[1]).val());  
                this.typetime = $("#"+this.timeAry[2]).val();  
                this.xTitle =  $("#"+this.timeAry[2]).find("option:selected").text();  
            }else{  
                this.categories = null;  
            }  
        }  
        /** 
         * 复制一个对象 
         * @param : chart     目标对象 
         * @param : typechart 指定类型 
         */  
        this.cloneAttr = function(chart){  
            this.title = chart.title;  
            this.subtitle = chart.subtitle;  
            this.xTitle = chart.xTitle;  
            this.yTitle = chart.yTitle;  
            this.typedb = chart.typedb;  
            this.typetime  = chart.typetime;  
            this.emtoys  = chart.emtoys;  
            this.smtoys = chart.smtoys;  
            this.categories = chart.categories;  
            this.categoriesLocal = chart.categoriesLocal;  
            this.timeAry = chart.timeAry;  
            return this;  
        }  
    }  
      
    // 统计图的触发绑定与整理  
    var MyHighcharts = function(options){  
        tempHighcharts = this;  
        var defaults = {  
                typedb:0,  
                title:"这是默认标题",  
                subTitle:"这是默认副标题",  
                xTitle:"x轴说明",  
                yTitle:"y轴说明",  
                line:null,  
                column:null,  
                pie:null  
            };  
        var options = $.extend(defaults, options);  
          
        /** ajax请求,这里用POST提交,因为参数可能拼接的较长 */  
        this.draw = function(i){  
            // 显示等待信息  
            $("#container").empty();  
            $("#container").append("<p style=\"text-align: center\"><img src=\"/images/loading.gif\" alt=\"加载中,请稍候...\" /></p>");  
            this.initLocalData(i,options);  
            $.post("/stat/chart!draw.do",this.initParams(tempChart),this.callBackChart);  
        }  
          
        /** 数据本地化请求*/     
        this.initLocalData = function(i,options){  
            switch (i) {  
            case 0:  
                tempChart = options.line;  
                break;  
            case 1:  
                tempChart = options.column;  
                break;  
            default:  
                tempChart = options.pie;  
                break;  
            }  
            tempChart.title = options.title;  
            tempChart.subtitle = options.subtitle;  
            tempChart.xTitle = options.xTitle;  
            tempChart.yTitle = options.yTitle;  
            tempChart.typedb = options.typedb;  
        }  
          
        /** 参数处理 */  
        this.initParams = function(myChart){  
            var param = new Object();  
            var timeStr = "1950#1950";  
              
            if(myChart.time != 0){  
                myChart.getPageTime();  
            }  
              
            param["myChart.typedb"] = myChart.typedb;  
            param["myChart.typechart"] = myChart.typechart;  
            param["myChart.typetime"] = myChart.typetime;  
              
            if(myChart.categories!=undefined && myChart.categories!=null){  
                param["myChart.categoriesStr"] = this.getFztoStr(myChart.categories);  
            }else{  
                timeStr = "";  
            }  
              
            if(myChart.emtoys!=undefined && myChart.emtoys!=null){            
                for(var i=0; i<myChart.emtoys.length; i++){  
                    param["myChart.emtoys["+i+"].name"] = myChart.emtoys[i].name;  
                    param["myChart.emtoys["+i+"].sql"] = myChart.emtoys[i].sql;  
                      
                    if(myChart.emtoys[i].params!=null && myChart.emtoys[i].params!=""){  
                        param["myChart.emtoys["+i+"].params"] = (timeStr==""?timeStr:timeStr+"#")+this.getFztoStr(myChart.emtoys[i].params);  
                    }else{  
                        if(timeStr != ""){  
                            param["myChart.emtoys["+i+"].params"] = timeStr;  
                        }  
                    }  
                }  
                for(var k=0; k<myChart.smtoys.length; k++){  
                    param["myChart.smtoys["+k+"].f"] = myChart.smtoys[k].f;  
                    param["myChart.smtoys["+k+"].name"] = myChart.smtoys[k].name;  
                    param["myChart.smtoys["+k+"].sql"] = myChart.smtoys[k].sql;  
                    param["myChart.smtoys["+k+"].params"] = "2010#2050"+myChart.smtoys[k].params  
                    if(myChart.smtoys[k].params!=null && myChart.smtoys[k].params!=""){  
                        param["myChart.smtoys["+k+"].params"] = timeStr==""?timeStr:(timeStr+"#")+this.getFztoStr(myChart.smtoys[k].params)  
                    }else{  
                        if(timeStr != ""){  
                            param["myChart.smtoys["+k+"].params"] = timeStr;  
                        }  
                    }  
                }  
            }     
              
            return param;  
        }  
          
        this.getFztoStr = function(array){  
            var str = "";  
            for(var i=0; i<array.length; i++){  
                if(i == 0){  
                    str = str+array[i];  
                }else{  
                    str = str+"#"+array[i];  
                }  
            }  
            return str;  
        }  
          
        /** 返回数据处理 */  
        this.callBackChart = function(data){  
            if(tempChart.timetype != 0){  
                tempChart.categoriesLocal = data.myChart.categoriesLocal;  
            }  
            switch (data.myChart.typechart) {  
            case 0://线状图  
                tempHighcharts.setClass(0);  
                tempHighcharts.callBackLine(data);  
                break;  
            case 1://柱状图  
                tempHighcharts.setClass(1);  
                tempHighcharts.callBackColumn(data);  
                break;  
            case 2://单饼图  
                tempHighcharts.setClass(2);  
                tempHighcharts.callBackPie(data);  
                break;  
            default://内嵌饼图  
                tempHighcharts.setClass(2);  
                tempHighcharts.callBackDonutPie(data);  
                break;  
            }  
        }  
          
        // line请求返回函数的处理  
        this.callBackLine = function(data){  
            new Highcharts.Chart({  
                chart: {  
                    renderTo: 'container',  
                    type: 'line'  
                },  
                title: {  
                    text: tempHighcharts.getTimeTitle(tempChart.categoriesLocal)  
                },  
                subtitle: {  
                    text: tempChart.subtitle  
                },  
                xAxis: {  
                    title: {  
                        text: tempChart.xTitle,  
                        align: 'high'  
                    },  
                    categories: tempChart.categoriesLocal  
                },  
                yAxis: {  
                    title: {  
                        align: 'high',  
                        offset: 0,  
                        text: tempChart.yTitle,  
                        rotation: 0,  
                        y: -10  
      
                    },  
                    plotLines: [{  
                        value: 0,  
                        width: 1,  
                        color: '#808080'  
                    }]  
                },  
                plotOptions: {  
                    spline: {  
                        marker: {  
                            radius: 4,  
                            lineColor: '#666666',  
                            lineWidth: 1  
                        }  
                    }  
                },  
                legend:{  
                    borderWidth:0  
                },  
                tooltip: {  
                    crosshairs: true,  
                    shared: true  
                      
                },  
                series: data.myChart.series  
            });  
        }  
        // column请求返回函数的处理  
        this.callBackColumn = function(data){  
            new Highcharts.Chart({  
                chart: {  
                    renderTo: 'container',  
                    type: 'column'  
                },  
                title: {  
                    text: tempHighcharts.getTimeTitle(tempChart.categoriesLocal)  
                },  
                subtitle: {  
                    text: tempChart.subtitle  
                },  
                xAxis: {  
                    title: {  
                        text: tempChart.xTitle,  
                        align: 'high'  
                    },  
                    categories: tempChart.categoriesLocal  
                },  
                yAxis: {  
                    title: {  
                        align: 'high',  
                        offset: 0,  
                        text: tempChart.yTitle,  
                        rotation: 0,  
                        y: -10  
      
                    },  
                    plotLines: [{  
                        value: 0,  
                        width: 1,  
                        color: '#808080'  
                    }]  
                },  
                plotOptions: {  
                    spline: {  
                        marker: {  
                            radius: 4,  
                            lineColor: '#666666',  
                            lineWidth: 1  
                        }  
                    }  
                },  
                legend:{  
                    borderWidth:0  
                },  
                tooltip: {  
                    formatter: function() {  
                        return ''+this.x+'<br/>'+  
                        this.series.name +': '+ this.y;  
                    }     
                },  
                series: data.myChart.series  
            });  
        }  
        // 单饼状图  
        this.callBackPie = function(data){  
            new Highcharts.Chart({  
                chart: {  
                    renderTo: 'container',  
                    plotBackgroundColor: null,  
                    plotBorderWidth: null,  
                    plotShadow: false,  
                    type:'pie'  
                },  
                title: {  
                    text: tempHighcharts.getTimeTitle(tempChart.categoriesLocal)  
                },  
                subtitle: {  
                    text: tempChart.subtitle  
                },  
                tooltip: {  
                    formatter: function() {  
                        return '<b>'+ this.point.name +'</b>: '+ Highcharts.numberFormat(this.percentage, 2) +' %';  
                    }  
                },  
                plotOptions: {  
                    pie: {  
                        allowPointSelect: true,  
                        cursor: 'pointer',  
                        dataLabels: {  
                            enabled: true,  
                            color: '#000000',  
                            connectorColor: '#000000',  
                            formatter: function() {  
                                return '<b>'+ this.point.name +'</b>: '+ Highcharts.numberFormat(this.percentage, 2) +' %';  
                            }  
                        }  
                    }  
                },  
                series: data.myChart.series_pie  
            });  
      
        }  
        // 内嵌饼状图  
        this.callBackDonutPie = function(data){  
            var dt = tempChart.comb(data);  
            new Highcharts.Chart({  
                chart: {  
                    renderTo: 'container',  
                    type: 'pie'  
                },  
                title: {  
                    text: tempHighcharts.getTimeTitle(data.myChart.categories)  
                },  
                subtitle: {  
                    text: tempChart.subtitle  
                },  
                yAxis: {  
                    title: {  
                        text: 'Total percent market share'  
                    }  
                },  
                plotOptions: {  
                    pie: {  
                        shadow: false  
                    }  
                },  
                tooltip: {  
                    formatter: function() {  
                        return '<b>'+ this.point.name +'</b>: '+ this.y +' %';  
                    }  
                },  
                series: dt  
            });  
        }  
        // 对内嵌饼状图异步请求产生的数据进行整理然后展示到JSP页面上  
        this.comb = function(data){  
            var colors = Highcharts.getOptions().colors;  
            var pie1 = data.myChart.series_pie[0];  
            var pie2 = data.myChart.series_pie[1];  
            var firstData = [];  
            var secondData = [];  
            for (var i = 0; i < pie1.data.length; i++) {  
                firstData.push({  
                    name: pie1.data[i][0],  
                    y: pie1.data[i][1],  
                    color: colors[i]  
                });  
            }  
            for (var i = 0; i < pie2.data.length; i++) {  
                secondData.push({  
                    name: pie2.data[i][0],  
                    y: pie2.data[i][1],  
                    color:this.getColor(colors,pie2,pie2.data[i])  
                });  
            }  
              
            var dt = [];  
            dt.push({  
                name: 'first',  
                data: firstData,  
                size: '60%',  
                dataLabels: {  
                    formatter: function() {  
                        return this.y > -1 ? this.point.name : null;  
                    },  
                    color: 'white',  
                    distance: -30  
                }  
            });  
            dt.push({  
                name: 'second',  
                data: secondData,  
                innerSize: '60%',  
                dataLabels: {  
                    formatter: function() {  
                        return this.y > -1 ? '<b>'+ this.point.name +':</b> '+ this.y +'%'  : null;  
                    }  
                }  
            });   
            return dt;  
        }  
        // 内嵌饼状图-子类的颜色  
        this.getColor = function(colors,pie2,dt){  
            var one = 0;  
            var all = 0;  
            var tempAy = [];  
            for (var i = 0; i < pie2.data.length; i++) {  
                if(pie2.data[i][2] == dt[2]){  
                    tempAy.push(pie2.data[i][0]);   
                }  
            }  
            all  =tempAy.length;  
            for (var i = 0; i < all; i++) {  
                if(tempAy[i]== dt[0]){  
                    one = i;  
                    continue;  
                }  
            }  
            //alert(dt[0]+":"+one+"/"+all+":"+dt[2]);  
            var brightness = 0.2 - (one / all) / 5 ;  
            return Highcharts.Color(colors[dt[2]]).brighten(brightness).get();  
        }  
        this.setClass = function(i){  
            var obj = $("#navigation a");  
            obj.attr("class","");  
            obj.eq(i).attr("class","current");  
        }  
        this.getTimeTitle = function(categories){  
            if(categories == null){  
                return tempChart.title;  
            }  
            var lgh = categories.length;  
            return this.pmt(categories[0],0)+"~"+this.pmt(categories[lgh-1],1)+tempChart.title;  
        }  
        this.pmt = function(str,i){//时间格式化        
            if(str.indexOf("#") != -1){  
                str = str.split("#")[i];  
            }  
            if(str.length==10){  
                str = str.substring(0,4)+"年"+str.substring(5,7)+"月"+str.substring(8)+"号";  
            }else if(str.length==7){  
                str = str.substring(0,4)+"年"+str.substring(5)+"月";  
            }else{  
                str = str + "年";  
            }  
            return str;  
        }  
    }  
      
    $(function(){  
        $("#navigation a").click(function(){  
            var i = $("#navigation a").index($(this));  
            tempHighcharts.draw(i);  
        });   
    });  

3.myChartTheme.js可以在官网下载,这里我作了少量改动,统计饼图的颜色作了调整

[javascript] view plaincopy

    /** 
     * Grid theme for Highcharts JS 
     * @author Torstein Hønsi 
     */  
      
    Highcharts.theme = {  
       colors: ['#058DC7', '#50B432', '#ED561B', '#DDDF00', '#24CBE5', '#64E572', '#FF9655', '#FFF263', '#6AF9C4'],  
       chart: {  
          backgroundColor: {  
             linearGradient: [0, 0, 500, 500],  
             stops: [  
                [0, 'rgb(255, 255, 255)'],  
                [1, 'rgb(240, 240, 255)']  
             ]  
          },  
          borderWidth: 2,  
          plotBackgroundColor: 'rgba(255, 255, 255, .9)',  
          plotShadow: true,  
          plotBorderWidth: 1,  
          spacingBottom:25  
       },  
       title: {  
          style: {  
             color: '#000',  
             font: 'bold 14px "Trebuchet MS", Verdana, sans-serif'  
          }  
       },  
       subtitle: {  
          style: {  
             color: '#666666',  
             font: 'bold 12px "Trebuchet MS", Verdana, sans-serif'  
          }  
       },  
       xAxis: {  
          gridLineWidth: 1,  
          lineColor: '#000',  
          tickColor: '#000',  
          labels: {  
             style: {  
                color: '#000',  
                font: '11px Trebuchet MS, Verdana, sans-serif'  
             }  
          },  
          title: {  
             style: {  
                color: '#333',  
                fontWeight: 'bold',  
                fontSize: '12px',  
                fontFamily: 'Trebuchet MS, Verdana, sans-serif'  
      
             }  
          }  
       },  
       yAxis: {  
          minorTickInterval: 'auto',  
          lineColor: '#000',  
          lineWidth: 1,  
          tickWidth: 1,  
          tickColor: '#000',  
          labels: {  
             style: {  
                color: '#000',  
                font: '11px Trebuchet MS, Verdana, sans-serif'  
             }  
          },  
          title: {  
             style: {  
                color: '#333',  
                fontWeight: 'bold',  
                fontSize: '12px',  
                fontFamily: 'Trebuchet MS, Verdana, sans-serif'  
             }  
          }  
       },  
       legend: {  
          itemStyle: {  
             font: '9pt Trebuchet MS, Verdana, sans-serif',  
             color: 'black'  
      
          },  
          itemHoverStyle: {  
             color: '#039'  
          },  
          itemHiddenStyle: {  
             color: 'gray'  
          }  
       },  
       labels: {  
          style: {  
             color: '#99b'  
          }  
       }  
    };  
      
    // Apply the theme  
    var highchartsOptions = Highcharts.setOptions(Highcharts.theme);  


4.sturts.xml

[html] view plaincopy

    <!-- 统计图ajax -->  
        <package name="fda.bak" namespace="/stat" extends="common-ajax">  
            <action name="chart" class="com.zjfda.action.MyChartAction">  
                <result type="json">  
                    chart\.categoriesLocal
    \d+
    ,  
                    chart\.series
    \d+
    \.name,  
                    chart\.series
    \d+
    \.data
    \d+
      
                    chart\.series_pie
    \d+
    \.name,  
                    chart\.series_pie
    \d+
    \.data
    \d+
    \d+
      
                </result>  
            </action>  
        </package>   


4.MyChartAction.java

[java] view plaincopy

    package com.zjfda.action;  
      
    import javax.annotation.Resource;  
      
    import com.common.ssh.action.BaseAction;  
    import com.zjfda.bean.MyChart;  
    import com.zjfda.service.MyChartService;  
      
    /** 
     * 线状、柱状、饼状(单饼与内嵌饼)统计图 
     * @author LQ. 
     * 
     */  
    @SuppressWarnings("serial")  
    public class MyChartAction extends BaseAction{  
      
        public String draw(){  
            myChart = myChartService.drawMyChart(myChart);  
            return SUCCESS;  
        }  
          
        @Resource MyChartService myChartService;  
        private MyChart myChart;  
        public MyChart getMyChart() {  
            return myChart;  
        }  
        public void setMyChart(MyChart myChart) {  
            this.myChart = myChart;  
        }  
          
    }  


5.MyChartService.java

[java] view plaincopy

    package com.zjfda.service;  
      
    import java.util.ArrayList;  
    import java.util.List;  
      
    import com.common.ssh.dao.BaseJDBCDao;  
    import com.common.ssh.service.BaseService;  
    import com.common.util.Tools;  
    import com.zjfda.bean.Emtoy;  
    import com.zjfda.bean.MyChart;  
    import com.zjfda.bean.Serie;  
    import com.zjfda.bean.Serie_pie;  
      
    public class MyChartService extends BaseService {  
      
        public MyChart drawMyChart(MyChart myChart) {  
            BaseJDBCDao dao;  
              
            /* TODO 如果是多数据源,在这里通过myChart.getTypedb()判断选择哪个数据源*/  
            switch (myChart.getTypedb()) {  
            case 1:  
                dao = spDao;  
                break;  
            default:  
                dao = fdaDao;  
                break;  
            }  
              
              
            switch (myChart.getTypechart()) {  
            case 3:  
                myChart = getPieInline(dao,myChart);  
                break;  
            case 2:  
                myChart = getPie(dao,myChart);  
                break;  
            case 1:  
                myChart = getColumn(dao,myChart);  
                break;  
            default:  
                myChart = getLine(dao,myChart);  
                break;  
            }  
            return myChart;  
        }  
      
        // 内嵌饼图  
        private MyChart getPieInline(BaseJDBCDao dao, MyChart myChart) {  
            List<Serie_pie> series_pie = new ArrayList<Serie_pie>();  
            String[] categories = getCombCate(myChart);  
            myChart.setCategoriesLocal(categories);  
            series_pie.add(getPieSeries(dao,myChart.getEmtoys(),categories));  
            series_pie.add(getPieSeries(dao,myChart.getSmtoys(),categories));  
            myChart.setSeries_pie(series_pie);  
            return myChart;  
        }  
      
        // 单饼图  
        private MyChart getPie(BaseJDBCDao dao, MyChart myChart) {  
            List<Serie_pie> series_pie = new ArrayList<Serie_pie>();  
            String[] categories = getCombCate(myChart);  
            myChart.setCategoriesLocal(categories);  
            series_pie.add(getPieSeries(dao,myChart.getEmtoys(),categories));  
            myChart.setSeries_pie(series_pie);  
            return myChart;  
        }  
          
        // 柱状图  
        private MyChart getColumn(BaseJDBCDao dao, MyChart myChart) {  
            return getLine(dao,myChart);  
        }  
      
        // 线状图  
        private MyChart getLine(BaseJDBCDao dao, MyChart myChart) {  
            List<Emtoy> emtoys = myChart.getEmtoys();  
            String[] categories = getCombCate(myChart);  
            myChart.setCategoriesLocal(categories);  
              
            List<Serie> series = new ArrayList<Serie>();  
            int length = categories.length;  
            int line[];  
            Serie serie;  
            for (Emtoy emtoy : emtoys) {  
                line = new int[length];  
                for (int i = 0; i < length; i++) {  
                    line[i] = dao.count(emtoy.getSql(), getRealParams(emtoy.getParams().split("#"),categories,i));  
                }  
                serie = new Serie();  
                serie.setName(emtoy.getName());  
                serie.setData(line);  
                series.add(serie);  
            }  
            myChart.setSeries(series);  
            return myChart;  
        }  
      
        // 饼图的数据处理  
        private Serie_pie getPieSeries(BaseJDBCDao dao, List<Emtoy> emtoys,String[] categories) {  
            int length = emtoys.size();  
            Object pie[][] = new Object[length][3];  
              
            Emtoy emtoy;  
            double temp;  
            for (int i = 0; i < length; i++) {  
                emtoy = emtoys.get(i);  
                temp = dao.count(emtoy.getSql(),getRealParamsPie(emtoy.getParams(),categories));      
                pie[i][0] = emtoy.getName();  
                pie[i][1] = temp;  
                pie[i][2] = emtoy.getF();  
            }  
            Serie_pie serie_pie = new Serie_pie();  
            serie_pie.setData(pie);  
            return serie_pie;  
        }  
      
        private Object[] getRealParamsPie(String paramsStr, String[] categories) {  
            String[] params;  
            if(paramsStr==null && categories==null){  
                params = new String[]{};  
            }else if(paramsStr!=null&&categories==null){  
                params = paramsStr.split("#");  
            }else if(paramsStr==null&&categories!=null){  
                params = new String[2];  
                int i = categories.length;  
                params = combTimeStart(params,categories[0]);  
                params = combTimeEnd(params,categories[i-1]);  
            }else{  
                params = paramsStr.split("#");  
                int i = categories.length;  
                params = combTimeStart(params,categories[0]);  
                params = combTimeEnd(params,categories[i-1]);  
            }  
            return params;  
        }  
      
        private String[] combTimeEnd(String[] params, String timeStr) {  
            int i = timeStr.indexOf("#");  
            if(i == -1){  
                params[1] = Tools.getTimeEnd(timeStr);  
            }else{  
                params[1] = Tools.getTimeEnd(timeStr.substring(i+1));  
            }  
            return params;  
        }  
      
        private String[] combTimeStart(String[] params, String timeStr) {  
            int i = timeStr.indexOf("#");  
            if(i == -1){  
                params[0] = Tools.getTimeStart(timeStr);  
            }else{  
                params[0] = Tools.getTimeStart(timeStr.substring(0,i));  
            }  
            return params;  
        }  
      
        // 取得查询参数  
        private Object[] getRealParams(Object[] params, String[] categories,int w) {  
            if(categories == null){  
                return params;  
            }  
            String timeStr;  
            if(categories.length == 1){  
                timeStr = categories[0];  
            }else{  
                timeStr = categories[w];  
            }  
              
            int i = timeStr.indexOf("#");  
            if(i == -1){  
                params[0] = Tools.getTimeStart(timeStr);  
                params[1] = Tools.getTimeEnd(timeStr);  
            }else{  
                params[0] = Tools.getTimeStart(timeStr.substring(0,i));  
                params[1] = Tools.getTimeEnd(timeStr.substring(i+1));  
            }  
            return params;  
        }  
        // 取得时间段  
        private String[] getCombCate(MyChart myChart) {  
            String[] newCates;  
            String[] cates = myChart.getCategories();  
            switch (myChart.getTypetime()) {  
            case 3:  
                newCates = Tools.getYearList(cates[0], cates[1]);  
                break;  
            case 2:  
                newCates = Tools.getMonthList(cates[0], cates[1]);  
                break;  
            case 1:  
                newCates = Tools.getDayList(cates[0], cates[1]);  
                break;  
            default:  
                newCates = cates;  
                break;  
            }  
            return newCates;  
        }  
    }  
6.统计图




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

应回复要求,贴出以下代码:

1.Tools.java

[java] view plaincopy

    package com.common.util;  
      
    import java.io.BufferedInputStream;  
    import java.io.BufferedOutputStream;  
    import java.io.File;  
    import java.io.FileInputStream;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.io.InputStream;  
    import java.io.OutputStream;  
    import java.security.MessageDigest;  
    import java.sql.Timestamp;  
    import java.text.DateFormat;  
    import java.text.SimpleDateFormat;  
    import java.util.ArrayList;  
    import java.util.Arrays;  
    import java.util.Calendar;  
    import java.util.Date;  
    import java.util.GregorianCalendar;  
    import java.util.List;  
    import java.util.Map;  
    import java.util.Random;  
    import java.util.regex.Matcher;  
    import java.util.regex.Pattern;  
      
    /** 
     * 常用工具 
     *  
     * <p>字段串的操作,时间操作,MD5加密,上传文件 
     */  
    public class Tools {  
      
        /** 
         * md5加密 
         * @param x 
         * @return 加密后的字符串 
         */  
        public static String md5(String x) {  
            try {  
                MessageDigest m = MessageDigest.getInstance("MD5");  
                m.update(x.getBytes("UTF8"));  
                byte s[] = m.digest();  
                String result = "";  
                for (int i = 0; i < s.length; i++) {  
                    result += Integer.toHexString((0x000000FF & s[i]) | 0xFFFFFF00)  
                            .substring(6);  
                }  
                return result;  
            } catch (Exception e) {  
                System.out.println("Tools.md5 加密[" + x + "]失败");  
                return null;  
            }  
        }  
          
        /** 
         * 格式化时间 
         * @param dateTime  要格式化的时间 
         * @param pattern   格式化的样式 
         * @return 已格式化的时间 
         */  
        public static String formatDateTime(Date dateTime, String pattern){  
            SimpleDateFormat dateFmt = new SimpleDateFormat(pattern);  
            return dateTime==null?"":dateFmt.format(dateTime);  
        }  
        /** 
         * 取得时间 
         * @param dateTime   
         * @return 返回"2007"数据格式的字符串 
         */  
        public static String miniTime(Date dateTime) {  
            return formatDateTime(dateTime,"yyyy");  
        }  
          
        /** 
         * 取得时间 
         * @param dateTime   
         * @return 返回"2007-09-10"数据格式的字符串 
         */  
        public static String shortTime(Date dateTime) {  
            return formatDateTime(dateTime,"yyyy-MM-dd");  
        }  
          
        /** 
         * 取得时间 
         * @param dateTime   
         * @return  返回"2007-09-10 16:09"数据格式的字符串 
         */  
        public static String middleTime(Date dateTime) {  
            return formatDateTime(dateTime,"yyyy-MM-dd HH:mm");  
        }  
          
        /** 
         * 取得时间 
         * @param dateTime   
         * @return 返回"2007-09-10 16:09:00"数据格式的字符串 
         */  
        public static String longTime(Date dateTime) {  
            return formatDateTime(dateTime,"yyyy-MM-dd HH:mm:ss");  
        }  
          
        /** 
         * 取得时间 
         * @param dateTime   
         * @return 返回"20070910160900"数据格式的字符串 
         */  
        public static String minLongTime(Date dateTime) {  
            return formatDateTime(dateTime,"yyyyMMddHHmmss");  
        }  
          
        /** 
         * 取得时间 
         * @param dateTime   
         * @return 返回"2007年09月10号 16点09分00秒"数据格式的字符串 
         */  
        public static String longZhTime(Date dateTime) {  
            return formatDateTime(dateTime,"yyyy年MM月dd号 HH点mm分ss秒");  
        }  
          
        /** 
         * 取得时间 
         * @param dateTime  
         * @return 返回"2007/09/10 16:09:00.000"数据格式的字符串 
         */  
        public static String bigLongTime(Date dateTime) {  
            return formatDateTime(dateTime,"yyyy/MM/dd HH:mm:ss.SSS");  
        }  
          
        /** 
         * 时间+-天数 :要得到的时间 
         * @param d      时间 
         * @param offset 天数 
         * @param bool  true天数加false天数减 
         * @return   
         */  
        public static Date changeDay(Date d,int offset,boolean bool){     
            Calendar calendar = Calendar.getInstance();     
            calendar.setTime(d);  
            if(bool){  
                calendar.set(Calendar.DAY_OF_YEAR,(calendar.get(Calendar.DAY_OF_YEAR) + offset));   
            }else{  
                calendar.set(Calendar.DAY_OF_YEAR,(calendar.get(Calendar.DAY_OF_YEAR) - offset));     
            }      
            return calendar.getTime();     
          }  
        /** 
         * 时间+-天数 :要得到的时间 
         * @param d      时间 
         * @param offset 天数 
         * @param bool  true天数加false天数减 
         * @return   
         */  
        public static Timestamp changeDay(Timestamp d,int offset,boolean bool){     
            Calendar calendar = Calendar.getInstance();     
            calendar.setTime(d);  
            if(bool){  
                calendar.set(Calendar.DAY_OF_YEAR,(calendar.get(Calendar.DAY_OF_YEAR) + offset));   
            }else{  
                calendar.set(Calendar.DAY_OF_YEAR,(calendar.get(Calendar.DAY_OF_YEAR) - offset));     
            }      
            return new Timestamp(calendar.getTimeInMillis());     
          }  
        /** 
         * 时间+-多少年 :要得到的时间 
         * @param d      时间 
         * @param offset 年数 
         * @param bool  true年数加false年数减 
         * @return   
         */  
        public static Date changeYear(Date d,int offset,boolean bool){     
            Calendar calendar = Calendar.getInstance();     
            calendar.setTime(d);  
            if(bool){  
                calendar.set(Calendar.YEAR,(calendar.get(Calendar.YEAR) + offset));   
            }else{  
                calendar.set(Calendar.YEAR,(calendar.get(Calendar.YEAR) - offset));     
            }      
            return calendar.getTime();     
          }  
        /** 
         * 时间+-多少年 :要得到的时间 
         * @param d      时间 
         * @param offset 年数 
         * @param bool  true年数加false年数减 
         * @return   
         */  
        public static Timestamp changeYear(Timestamp d,int offset,boolean bool){     
            Calendar calendar = Calendar.getInstance();     
            calendar.setTime(d);  
            if(bool){  
                calendar.set(Calendar.YEAR,(calendar.get(Calendar.YEAR) + offset));   
            }else{  
                calendar.set(Calendar.YEAR,(calendar.get(Calendar.YEAR) - offset));     
            }      
            return new Timestamp(calendar.getTimeInMillis());     
          }  
          
          
        /** 
         * 字符串是否可以转化成Double形式 
         * @param str 
         * @return 
         */  
        public static boolean isDouble(String str){  
            Pattern pattern = Pattern.compile("^[-\\+]?\\d+(\\.\\d*)?|\\.\\d+$");    
            return pattern.matcher(str).matches();  
        }  
        /** 
         * 是否可以转化为整数 
         * @param str 
         * @return 
         */  
        public static boolean isInteger(String str){  
            Pattern pattern = Pattern.compile("[0-9]*");  
            return pattern.matcher(str).matches();    
        }   
          
        /** 
         * 是否可以转化为整数加字符串 
         * @param str 
         * @return 
         */  
        public static boolean isStr(String str){  
            Pattern pattern = Pattern.compile("[a-zA-Z0-9]*");  
            return pattern.matcher(str).matches();    
        }   
          
        /** 
         * 是否可以转化为数字 
         * @param str 
         * @return 
         */  
        public static boolean isNumber(String str){  
            return isInteger(str) || isDouble(str);    
        }   
          
        /** 
         * 取得结束的时间 
         * 如果参数为2007,则返回2007-12-31 23:59:59 
         * 如果参数为2007-08 ,则返回2007-08-31 23:59:59 
         * 如果参数为2007-09 ,则返回2007-09-30 23:59:59 
         * 如果参数为2007-09-09 ,则返回2007-09-09 23:59:59 
         * @param time yyyy yyyy-MM yyyy-MM-dd形式 
         * @return yyyy-MM-dd HH:mm:ss 
         */  
        public static String getTimeEnd(final String time){  
            Calendar timeEnd = Calendar.getInstance();     
            if(time!=null){  
                if(time.length()==4){  
                    timeEnd.set(Calendar.YEAR, Integer.parseInt(time));  
                    timeEnd.set(Calendar.MONTH,11);     
                    timeEnd.set(Calendar.DATE, 1);     
                    timeEnd.roll(Calendar.DATE, -1);     
                    timeEnd.set(Calendar.HOUR_OF_DAY, 23);     
                    timeEnd.set(Calendar.MINUTE, 59);     
                    timeEnd.set(Calendar.SECOND, 59);  
                }  
                if(time.length()==7){  
                    timeEnd.set(Calendar.YEAR, Integer.parseInt((time.split("-"))[0]));  
                    timeEnd.set(Calendar.MONTH,Integer.parseInt((time.split("-"))[1])-1);     
                    timeEnd.set(Calendar.DATE, 1);     
                    timeEnd.roll(Calendar.DATE, -1);     
                    timeEnd.set(Calendar.HOUR_OF_DAY, 23);     
                    timeEnd.set(Calendar.MINUTE, 59);     
                    timeEnd.set(Calendar.SECOND, 59);  
                }  
                if(time.length()==10){  
                    timeEnd.set(Calendar.YEAR, Integer.parseInt((time.split("-"))[0]));  
                    timeEnd.set(Calendar.MONTH,Integer.parseInt((time.split("-"))[1])-1);     
                    timeEnd.set(Calendar.DATE, Integer.parseInt((time.split("-"))[2]));     
                    timeEnd.set(Calendar.HOUR_OF_DAY, 23);     
                    timeEnd.set(Calendar.MINUTE, 59);     
                    timeEnd.set(Calendar.SECOND, 59);  
                }  
            }  
            return formatDateTime(timeEnd.getTime(),"yyyy-MM-dd HH:mm:ss");  
        }  
          
        /** 
         * 取得起始的时间 
         * 如果参数为2007,则返回2007-01-01 00:00:00 
         * 如果参数为2007-09 ,则返回2007-09-01 00:00:00 
         * 如果参数为2007-09-09 ,则返回2007-09-09 00:00:00 
         * @param time yyyy yyyy-MM yyyy-MM-dd形式 
         * @return yyyy-MM-dd HH:mm:ss 
         */  
        public static String getTimeStart(final String time){  
            Calendar timeStart = Calendar.getInstance();     
            if(time!=null){  
                if(time.length()==4){  
                    timeStart.set(Calendar.YEAR, Integer.parseInt(time));  
                    timeStart.set(Calendar.MONTH,0);     
                    timeStart.set(Calendar.DATE, 1);     
                    timeStart.set(Calendar.HOUR_OF_DAY, 0);     
                    timeStart.set(Calendar.MINUTE, 0);     
                    timeStart.set(Calendar.SECOND, 0);  
                }  
                if(time.length()==7){  
                    timeStart.set(Calendar.YEAR, Integer.parseInt((time.split("-"))[0]));  
                    timeStart.set(Calendar.MONTH,Integer.parseInt((time.split("-"))[1])-1);     
                    timeStart.set(Calendar.DATE, 1);     
                    timeStart.set(Calendar.HOUR_OF_DAY, 0);     
                    timeStart.set(Calendar.MINUTE, 0);     
                    timeStart.set(Calendar.SECOND, 0);  
                }  
                if(time.length()==10){  
                    timeStart.set(Calendar.YEAR, Integer.parseInt((time.split("-"))[0]));  
                    timeStart.set(Calendar.MONTH,Integer.parseInt((time.split("-"))[1])-1);     
                    timeStart.set(Calendar.DATE, Integer.parseInt((time.split("-"))[2]));     
                    timeStart.set(Calendar.HOUR_OF_DAY, 0);     
                    timeStart.set(Calendar.MINUTE, 0);     
                    timeStart.set(Calendar.SECOND, 0);  
                }  
                  
            }  
            return formatDateTime(timeStart.getTime(),"yyyy-MM-dd HH:mm:ss");  
        }  
          
        /** 
         * 判断str是否为空或为all,成立返回false,反之返回true 
         * @param str 
         * @return 
         */  
        public static boolean isActive(String str){  
            return str!=null && !str.trim().equals("all") && !str.trim().equals("");  
        }  
          
        /** 
         * 判断str是否为空或为空字符串,成立返回false,反之返回true 
         * @param str 
         * @return 
         */  
        public static boolean isNullChar(String str){  
            return str!=null && !str.trim().equals("");  
        }  
          
        /** 
         * yyyy-MM-dd hh:mm:ss转换为date类型 
         * @param str 
         */  
        public static Date getDate(String str) {    
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");    
            Date date = null;  
            try {    
                date = df.parse(str);    
            } catch (Exception e) {    
                System.out.println("Tools.getDate失败");  
            }    
            return date;  
        }    
          
        /** 
         * 数据处理,保留precision位小数 
         * @param val 要处理的数字 
         * @param precision 要保留的小数位数 
         * @return 
         */  
        public static Double roundDouble(double val, int precision) {    
            Double ret = null;    
            try {    
                double factor = Math.pow(10, precision);    
                ret = Math.floor(val * factor + 0.5) / factor;    
            } catch (Exception e) {    
                e.printStackTrace();    
            }    
            return ret;    
        }   
          
        /**  
        * 获得随机数字符串  
        *  
        * @param length  
        *            需要获得随机数的长度  
        * @param type  
        *            随机数的类型:'0':表示仅获得数字随机数;'1':表示仅获得字符随机数;'2':表示获得数字字符混合随机数  
        * @return  
        */  
        public static String getRandomStr(int length, int type) {  
            String strRandom = "";  
            Random rnd = new Random();  
            if (length < 0)   
                length = 5;  
            if ((type > 2) || (type < 0))   
                type = 2;  
            switch (type) {  
            case 0:  
                for (int iLoop = 0; iLoop < length; iLoop++) {  
                    strRandom += Integer.toString(rnd.nextInt(10));  
                }  
                break;  
            case 1:  
                for (int iLoop = 0; iLoop < length; iLoop++) {  
                    strRandom += Integer.toString((35 - rnd.nextInt(10)), 36);  
                }  
                break;  
            case 2:  
                for (int iLoop = 0; iLoop < length; iLoop++) {  
                    strRandom += Integer.toString(rnd.nextInt(36), 36);  
                }  
                break;  
            }  
            return strRandom;  
        }  
          
        public static boolean chkInputByRegex(String inputString,String regexString){  
            Pattern p=Pattern.compile(regexString);  
            Matcher m=p.matcher(inputString);  
            return m.matches();       
        }  
        /** 
         * 取得时间段内的年的日期集合 
         * @param beginDateStr 
         * @param endDateStr 
         * @return 
         */  
        public static String[] getYearList(String dateFrom, String dateEnd) {  
            dateFrom = dateFrom.substring(0,4);  
            dateEnd = dateEnd.substring(0,4);  
            int df = Integer.valueOf(dateFrom);  
            int de = Integer.valueOf(dateEnd);  
            List<String> dateList = new ArrayList<String>();  
            for (int i = df; i <= de; i++) {  
                dateList.add(""+i);  
            }  
            String[] dateArray = new String[dateList.size()];  
            dateList.toArray(dateArray);  
            return dateArray;  
        }  
        /** 
         * 取得时间段内的月的日期集合 
         * @param beginDateStr 
         * @param endDateStr 
         * @return 
         */  
        public static String[] getMonthList(String dateFrom, String dateEnd) {  
            //指定要解析的时间格式  
            SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");  
            List<String> dateList = new ArrayList<String>();  
            //定义一些变量  
            Date beginDate = null;  
            Date endDate = null;  
            GregorianCalendar beginGC = null;  
            GregorianCalendar endGC = null;  
            try {  
                //将字符串parse成日期  
                beginDate = f.parse(dateFrom);  
                endDate = f.parse(dateEnd);  
                //设置日历  
                beginGC = new GregorianCalendar();   
                beginGC.setTime(beginDate);   
                endGC = new GregorianCalendar();   
                endGC.setTime(endDate);  
                //直到两个时间相同  
                while(beginGC.getTime().compareTo(endGC.getTime())<=0){  
                    dateList.add(beginGC.get(Calendar.YEAR) + "-" + getM(beginGC.get(Calendar.MONTH)+1));  
                    //以月为单位,增加时间  
                    beginGC.add(Calendar.MONTH,1);  
                }  
                dateList.add(beginGC.get(Calendar.YEAR) + "-" + getM(beginGC.get(Calendar.MONTH)+1));  
                String[] dateArray = new String[dateList.size()];  
                dateList.toArray(dateArray);  
                return dateArray;  
            }  
            catch(Exception e) {  
                e.printStackTrace();  
                return null;  
            }  
        }  
        private static String getM(int i) {  
            String st = ""+i;  
            st = "00".substring(st.length())+st;  
            return st;  
        }  
      
        /** 
         * 取得时间段内的日的日期集合 
         * @param dateFrom 
         * @param dateEnd 
         * @return 
         */  
        public static String[] getDayList(String dateFrom, String dateEnd){  
            long time = 1l;  
            long perDayMilSec = 24 * 60 * 60 * 1000;  
            List<String> dateList = new ArrayList<String>();  
            dateList.add(dateFrom);  
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");  
            while (true) {  
                try {  
                    time = sdf.parse(dateFrom).getTime();  
                    time = time + perDayMilSec;  
                    dateFrom = sdf.format(new Date(time));  
                    if (dateFrom.compareTo(dateEnd) < 0) {  
                        dateList.add(dateFrom);  
                    } else {  
                        break;  
                    }  
                } catch (Exception e) {  
                    e.printStackTrace();  
                    break;  
                }  
            }  
            dateList.add(dateEnd);  
            String[] dateArray = new String[dateList.size()];  
            dateList.toArray(dateArray);  
            return dateArray;  
      
        }  
      
        /** 
         * 取得map中的真实值 
         * @param map 
         * @param key 
         * @return 
         */  
        public static String getValue(Map<String, Object> map, String key) {  
            Object obj = map.get(key);  
            if(obj == null)  
                return null;  
            else if(!obj.getClass().isArray())  
                return obj.toString();  
            else  
                return Arrays.toString((Object[])obj).replace("[", "").replace("]", "");  
        }  
          
        public static void main(String args[]){  
        }  
          
    }  

2.MyChart.java

[java] view plaincopy

    package com.zjfda.bean;  
      
    import java.util.List;  
    import java.util.Map;  
      
    /** 
     * 数据统计图参数与结果的传递封装 
     * @author LQ 
     */  
    public class MyChart {  
      
        /*  
         * 指定数据源  
         * 适用于数据统计来自多个数据源时 
         */  
        private int typedb;  
          
        /* 
         * 统计类型 
         * 0线状图,1柱状图,2单饼图,3内嵌饼图 
         */  
        private int typechart;  
          
        /* 
         * 统计时间类型 
         * 0自定义,1天,2月,3年 
         */  
        private int typetime;  
          
        // 封装Line,column传递到jsp页面的数据  
        public List<Serie> series;  
        public List<Serie_pie> series_pie;  
          
        /* 
         * 封装统计查询参数  
         */  
        private List<Emtoy> emtoys;  
        private List<Emtoy> smtoys;//如果是内嵌饼图,这个参数封装的是内嵌饼图的参数数据  
          
        /* 
         * 封装y轴类型时间类型 
         */  
        private String[] categories;//时间参数  
        private String[] categoriesLocal;//时间参数  
        private String categoriesStr;  
          
        /* 
         * 封装自定义的参数 
         */  
        private Map<String,Object> map;  
          
      
        public int getTypedb() {  
            return typedb;  
        }  
        public void setTypedb(int typedb) {  
            this.typedb = typedb;  
        }  
        public int getTypechart() {  
            return typechart;  
        }  
        public void setTypechart(int typechart) {  
            this.typechart = typechart;  
        }  
        public int getTypetime() {  
            return typetime;  
        }  
        public void setTypetime(int typetime) {  
            this.typetime = typetime;  
        }  
        public List<Serie> getSeries() {  
            return series;  
        }  
        public void setSeries(List<Serie> series) {  
            this.series = series;  
        }  
        public List<Serie_pie> getSeries_pie() {  
            return series_pie;  
        }  
        public void setSeries_pie(List<Serie_pie> series_pie) {  
            this.series_pie = series_pie;  
        }  
        public List<Emtoy> getEmtoys() {  
            return emtoys;  
        }  
        public void setEmtoys(List<Emtoy> emtoys) {  
            this.emtoys = emtoys;  
        }  
        public List<Emtoy> getSmtoys() {  
            return smtoys;  
        }  
        public void setSmtoys(List<Emtoy> smtoys) {  
            this.smtoys = smtoys;  
        }  
        public Map<String, Object> getMap() {  
            return map;  
        }  
        public void setMap(Map<String, Object> map) {  
            this.map = map;  
        }  
        public String[] getCategories() {  
            if(categoriesStr==null || categoriesStr.equals("")){  
                return null;  
            }  
            if(categoriesStr.indexOf("#")!=-1){  
                return categoriesStr.split("#");  
            }else{  
                return new String[]{categoriesStr};  
            }  
        }  
        public void setCategories(String[] categories) {  
            this.categories = categories;  
        }  
        public String[] getCategoriesLocal() {  
            return categoriesLocal;  
        }  
        public void setCategoriesLocal(String[] categoriesLocal) {  
            this.categoriesLocal = categoriesLocal;  
        }  
        public String getCategoriesStr() {  
            return categoriesStr;  
        }  
        public void setCategoriesStr(String categoriesStr) {  
            this.categoriesStr = categoriesStr;  
        }  
    }  




3.Emtoy.java

[java] view plaincopy

    package com.zjfda.bean;  
      
    /** 
     * 封装统计参数 
     * @author LQ 
     * 
     */  
    public class Emtoy{  
        // 标志  
        public int f;  
        // 系列名称  
        public String name;  
        // 查询语句  
        public String sql;  
        // 查询参数  
        public String params;  
      
        public int getF() {  
            return f;  
        }  
        public void setF(int f) {  
            this.f = f;  
        }  
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public String getSql() {  
            return sql;  
        }  
        public void setSql(String sql) {  
            this.sql = sql;  
        }  
        public String getParams() {  
            return params;  
        }  
        public void setParams(String params) {  
            this.params = params;  
        }  
        public Emtoy() {  
            super();  
        }  
    }  

4.Serie.java

[java] view plaincopy

    package com.zjfda.bean;  
      
    /** 
     * 封装展示的统计数据 
     * @author LQ 
     * 
     */  
    public class Serie {  
        // 系列名称  
        public String name;  
        // 封装线状图或柱状图数据  
        public int data[];  
        public String getName() {  
            return name;  
        }  
        public void setName(String name) {  
            this.name = name;  
        }  
        public int[] getData() {  
            return data;  
        }  
        public void setData(int[] data) {  
            this.data = data;  
        }  
    }  


5,Serie_pie.java

[java] view plaincopy

    package com.zjfda.bean;  
    /** 
     * 封装展示的统计数据 
     * @author LQ 
     * 
     */  
    public class Serie_pie {  
    // 系列名称  
escape()、encodeURI()、encodeURIComponent()区别详解 js
JavaScript中有三个可以对字符串编码的函数,分别是: escape,encodeURI,encodeURIComponent,相应3个解码函数:unescape,decodeURI,decodeURIComponent 。

下面简单介绍一下它们的区别

1 escape()函数

定义和用法 
escape() 函数可对字符串进行编码,这样就可以在所有的计算机上读取该字符串。

语法 
escape(string)

参数  描述  
string  必需。要被转义或编码的字符串。 

返回值 
已编码的 string 的副本。其中某些字符被替换成了十六进制的转义序列。

说明 
该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( ) 。其他所有的字符都会被转义序列替换。

 


2 encodeURI()函数 
定义和用法 
encodeURI() 函数可把字符串作为 URI 进行编码。

语法 
encodeURI(URIstring)

参数  描述  
URIstring  必需。一个字符串,含有 URI 或其他要编码的文本。 

返回值 
URIstring 的副本,其中的某些字符将被十六进制的转义序列进行替换。

说明 
该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( ) 。

该方法的目的是对 URI 进行完整的编码,因此对以下在 URI 中具有特殊含义的 ASCII 标点符号,encodeURI() 函数是不会进行转义的:;/?:@&=+$,#

 


3 encodeURIComponent() 函数

定义和用法 
encodeURIComponent() 函数可把字符串作为 URI 组件进行编码。

语法 
encodeURIComponent(URIstring)

参数  描述  
URIstring  必需。一个字符串,含有 URI 组件或其他要编码的文本。 

返回值 
URIstring 的副本,其中的某些字符将被十六进制的转义序列进行替换。

说明 
该方法不会对 ASCII 字母和数字进行编码,也不会对这些 ASCII 标点符号进行编码: - _ . ! ~ * ' ( ) 。

其他字符(比如 :;/?:@&=+$,# 这些用于分隔 URI 组件的标点符号),都是由一个或多个十六进制的转义序列替换的。

提示和注释 
提示:请注意 encodeURIComponent() 函数 与 encodeURI() 函数的区别之处,前者假定它的参数是 URI 的一部分(比如协议、主机名、路径或查询字符串)。因此 encodeURIComponent() 函数将转义用于分隔 URI 各个部分的标点符号。

 

4 总结:

 通过对三个函数的分析,我们可以知道:escape()除了 ASCII 字母、数字和特定的符号外,对传进来的字符串全部进行转义编码,因此如果想对URL编码,最好不要使用此方法。而encodeURI() 用于编码整个URI,因为URI中的合法字符都不会被编码转换。encodeURIComponent方法在编码单个URIComponent(指请求参数)应当是最常用的,它可以讲参数中的中文、特殊字符进行转义,而不会影响整个URL。

 

5 示例:

1 escape()

<script type="text/javascript">

document.write(escape("http://www.w3school.com.cn/") + "<br />")

document.write(escape("?!=()#%&"))

</script>输出:

http%3A//www.w3school.com.cn

%3F%21%3D%28%29%23%25%26

2 encodeURI()

<script type="text/javascript">

document.write(encodeURI("http://www.w3school.com.cn/")+ "<br />")

document.write(encodeURI("http://www.w3school.com.cn/My first/"))

document.write(encodeURI(",/?:@&=+$#"))

</script>输出:

http://www.w3school.com.cn/

http://www.w3school.com.cn/My%20first/

,/?:@&=+$#

对整个URL进行编码,而URL的特定标识符不会被转码。

3 encodeURIComponent()

例1:

<script type="text/javascript">

document.write(encodeURIComponent("http://www.w3school.com.cn/"))

document.write("<br />")

document.write(encodeURIComponent("http://www.w3school.com.cn/p 1/"))

document.write("<br />")

document.write(encodeURIComponent(",/?:@&=+$#"))

</script输出:

http%3A%2F%2Fwww.w3school.com.cn 
http%3A%2F%2Fwww.w3school.com.cn%2Fp%201%2F 
%2C%2F%3F%3A%40%26%3D%2B%24%23
例2:<script language="javascript">document.write('

<a href="http://passport.baidu.com/?logout&aid=7&u='+encodeURIComponent("http://cang.baidu.com/bruce42")+'">退出</a>');</script>

对URL中的参数进行编码,因为参数也是一个URL,如果不编码会影响整个URL的跳转。
淘宝(大数据库应用) hadoop http://blog.sina.com.cn/s/blog_62804427010182ab.html
第一部分、mapreduce模式与hadoop框架深入浅出
架构扼要
         想读懂此文,读者必须先要明确以下几点,以作为阅读后续内容的基础知识储备:
Mapreduce是一种模式。
Hadoop是一种框架。
Hadoop是一个实现了mapreduce模式的开源的分布式并行编程框架。
    所以,你现在,知道了什么是mapreduce,什么是hadoop,以及这两者之间最简单的联系,而本文的主旨即是,一句话概括:在hadoop的框架上采取mapreduce的模式处理海量数据。下面,咱们可以依次深入学习和了解mapreduce和hadoop这两个东西了。
Mapreduce模式
    前面说了,mapreduce是一种模式,一种什么模式呢?一种云计算的核心计算模式,一种分布式运算技术,也是简化的分布式编程模式,它主要用于解决问题的程序开发模型,也是开发人员拆解问题的方法。
    Ok,光说不上图,没用。如下图所示,mapreduce模式的主要思想是将自动分割要执行的问题(例如程序)拆解成map(映射)和reduce(化简)的方式,流程图如下图1所示:

    在数据被分割后通过Map 函数的程序将数据映射成不同的区块,分配给计算机机群处理达到分布式运算的效果,在通过Reduce 函数的程序将结果汇整,从而输出开发者需要的结果。
    MapReduce 借鉴了函数式程序设计语言的设计思想,其软件实现是指定一个Map 函数,把键值对(key/value)映射成新的键值对(key/value),形成一系列中间结果形式的key/value 对,然后把它们传给Reduce(规约)函数,把具有相同中间形式key 的value 合并在一起。Map 和Reduce 函数具有一定的关联性。函数描述如表1 所示:

    MapReduce致力于解决大规模数据处理的问题,因此在设计之初就考虑了数据的局部性原理,利用局部性原理将整个问题分而治之。MapReduce集群由普通PC机构成,为无共享式架构。在处理之前,将数据集分布至各个节点。处理时,每个节点就近读取本地存储的数据处理(map),将处理后的数据进行合并(combine)、排序(shuffle and sort)后再分发(至reduce节点),避免了大量数据的传输,提高了处理效率。无共享式架构的另一个好处是配合复制(replication)策略,集群可以具有良好的容错性,一部分节点的down机对集群的正常工作不会造成影响。
    ok,你可以再简单看看下副图,整幅图是有关hadoop的作业调优参数及原理,图的左边是MapTask运行示意图,右边是ReduceTask运行示意图:

    如上图所示,其中map阶段,当map task开始运算,并产生中间数据后并非直接而简单的写入磁盘,它首先利用内存buffer来对已经产生的buffer进行缓存,并在内存buffer中进行一些预排序来优化整个map的性能。而上图右边的reduce阶段则经历了三个阶段,分别Copy->Sort->reduce。我们能明显的看出,其中的Sort是采用的归并排序,即merge sort。
    了解了什么是mapreduce,接下来,咱们可以来了解实现了mapreduce模式的开源框架—hadoop。
Hadoop框架
    前面说了,hadoop是一个框架,一个什么样的框架呢?Hadoop 是一个实现了MapReduce 计算模型的开源分布式并行编程框架,程序员可以借助Hadoop 编写程序,将所编写的程序运行于计算机机群上,从而实现对海量数据的处理。
    此外,Hadoop 还提供一个分布式文件系统(HDFS)及分布式数据库(HBase)用来将数据存储或部署到各个计算节点上。所以,你可以大致认为:Hadoop=HDFS(文件系统,数据存储技术相关)+HBase(数据库)+MapReduce(数据处理)。Hadoop 框架如图2 所示:

    借助Hadoop 框架及云计算核心技术MapReduce 来实现数据的计算和存储,并且将HDFS 分布式文件系统和HBase 分布式数据库很好的融入到云计算框架中,从而实现云计算的分布式、并行计算和存储,并且得以实现很好的处理大规模数据的能力。
Hadoop的组成部分
    我们已经知道,Hadoop是Google的MapReduce一个Java实现。MapReduce是一种简化的分布式编程模式,让程序自动分布到一个由普通机器组成的超大集群上并发执行。Hadoop主要由HDFS、MapReduce和HBase等组成。具体的hadoop的组成如下图:

    由上图,我们可以看到:
    1、             Hadoop HDFS是Google GFS存储系统的开源实现,主要应用场景是作为并行计算环境(MapReduce)的基础组件,同时也是BigTable(如HBase、HyperTable)的底层分布式文件系统。HDFS采用master/slave架构。一个HDFS集群是有由一个Namenode和一定数目的Datanode组成。Namenode是一个中心服务器,负责管理文件系统的namespace和客户端对文件的访问。Datanode在集群中一般是一个节点一个,负责管理节点上它们附带的存储。在内部,一个文件其实分成一个或多个block,这些block存储在Datanode集合里。如下图所示(HDFS体系结构图):

    2、             Hadoop MapReduce是一个使用简易的软件框架,基于它写出来的应用程序能够运行在由上千个商用机器组成的大型集群上,并以一种可靠容错的方式并行处理上TB级别的数据集。
    一个MapReduce作业(job)通常会把输入的数据集切分为若干独立的数据块,由 Map任务(task)以完全并行的方式处理它们。框架会对Map的输出先进行排序,然后把结果输入给Reduce任务。通常作业的输入和输出都会被存储在文件系统中。整个框架负责任务的调度和监控,以及重新执行已经失败的任务。如下图所示(Hadoop MapReduce处理流程图):

    3、             Hive是基于Hadoop的一个数据仓库工具,处理能力强而且成本低廉。
主要特点:
存储方式是将结构化的数据文件映射为一张数据库表。提供类SQL语言,实现完整的SQL查询功能。可以将SQL语句转换为MapReduce任务运行,十分适合数据仓库的统计分析。
不足之处:
采用行存储的方式(SequenceFile)来存储和读取数据。效率低:当要读取数据表某一列数据时需要先取出所有数据然后再提取出某一列的数据,效率很低。同时,它还占用较多的磁盘空间。
由于以上的不足,有人(查礼博士)介绍了一种将分布式数据处理系统中以记录为单位的存储结构变为以列为单位的存储结构,进而减少磁盘访问数量,提高查询处理性能。这样,由于相同属性值具有相同数据类型和相近的数据特性,以属性值为单位进行压缩存储的压缩比更高,能节省更多的存储空间。如下图所示(行列存储的比较图):

4、             HBase
    HBase是一个分布式的、面向列的开源数据库,它不同于一般的关系数据库,是一个适合于非结构化数据存储的数据库。另一个不同的是HBase基于列的而不是基于行的模式。HBase使用和 BigTable非常相同的数据模型。用户存储数据行在一个表里。一个数据行拥有一个可选择的键和任意数量的列,一个或多个列组成一个ColumnFamily,一个Fmaily下的列位于一个HFile中,易于缓存数据。表是疏松的存储的,因此用户可以给行定义各种不同的列。在HBase中数据按主键排序,同时表按主键划分为多个HRegion,如下图所示(HBase数据表结构图):

    Ok,行文至此,看似洋洋洒洒近千里,但若给读者造成阅读上的负担,则不是我本意。接下来的内容,我不会再引用诸多繁杂的专业术语,以给读者心里上造成不良影响。
    我再给出一副图,算是对上文所说的hadoop框架及其组成部分做个总结,如下图所示,便是hadoop的内部结构,我们可以看到,海量的数据交给hadoop处理后,在hadoop的内部中,正如上文所述:hadoop提供一个分布式文件系统(HDFS)及分布式数据库(Hbase)用来存储或部署到各个计算点上,最终在内部采取mapreduce的模式对其数据进行处理,然后输出处理结果:

第二部分、淘宝海量数据产品技术架构解读—学习海量数据处理经验
    在上面的本文的第一部分中,我们已经对mapreduce模式及hadoop框架有了一个深入而全面的了解。不过,如果一个东西,或者一个概念不放到实际应用中去,那么你对这个理念永远只是停留在理论之内,无法向实践迈进。
    Ok,接下来,本文的第二部分,咱们以淘宝的数据魔方技术架构为依托,通过介绍淘宝的海量数据产品技术架构,来进一步学习和了解海量数据处理的经验。
淘宝海量数据产品技术架构
    如下图2-1所示,即是淘宝的海量数据产品技术架构,咱们下面要针对这个架构来一一剖析与解读。
    相信,看过本博客内其它文章的细心读者,定会发现,图2-1最初见于本博客内的此篇文章:从几幅架构图中偷得半点海量数据处理经验之上,同时,此图2-1最初发表于《程序员》8月刊,作者:朋春。
    在此之前,有一点必须说明的是:本文下面的内容大都是参考自朋春先生的这篇文章:淘宝数据魔方技术架构解析所写,我个人所作的工作是对这篇文章的一种解读与关键技术和内容的抽取,以为读者更好的理解淘宝的海量数据产品技术架构。与此同时,还能展示我自己读此篇的思路与感悟,顺带学习,何乐而不为呢?。
    Ok,不过,与本博客内之前的那篇文章(几幅架构图中偷得半点海量数据处理经验)不同,本文接下来,要详细阐述这个架构。我也做了不少准备工作(如把这图2-1打印了下来,经常琢磨):
 
                                              图2-1 淘宝海量数据产品技术架构
    好的,如上图所示,我们可以看到,淘宝的海量数据产品技术架构,分为以下五个层次,从上至下来看,它们分别是:数据源,计算层,存储层,查询层和产品层。我们来一一了解这五层:
数据来源层。存放着淘宝各店的交易数据。在数据源层产生的数据,通过DataX,DbSync和Timetunel准实时的传输到下面第2点所述的“云梯”。
计算层。在这个计算层内,淘宝采用的是hadoop集群,这个集群,我们暂且称之为云梯,是计算层的主要组成部分。在云梯上,系统每天会对数据产品进行不同的mapreduce计算。
存储层。在这一层,淘宝采用了两个东西,一个使MyFox,一个是Prom。MyFox是基于MySQL的分布式关系型数据库的集群,Prom是基于hadoop Hbase技术 的(读者可别忘了,在上文第一部分中,咱们介绍到了这个hadoop的组成部分之一,Hbase—在hadoop之内的一个分布式的开源数据库)的一个NoSQL的存储集群。
查询层。在这一层中,有一个叫做glider的东西,这个glider是以HTTP协议对外提供restful方式的接口。数据产品通过一个唯一的URL来获取到它想要的数据。同时,数据查询即是通过MyFox来查询的。下文将具体介绍MyFox的数据查询过程。
 产品层。简单理解,不作过多介绍。
    接下来,咱们重点来了解第三层-存储层中的MyFox与Prom,然后会稍带分析下glide的技术架构,最后,再了解下缓存。文章即宣告结束。
    我们知道,关系型数据库在我们现在的工业生产中有着广泛的引用,它包括Oracle,MySQL、DB2、Sybase和SQL Server等等。
MyFOX
    淘宝选择了MySQL的MyISAM引擎作为底层的数据存储引擎。且为了应对海量数据,他们设计了分布式MySQL集群的查询代理层-MyFOX。
如下图所示,是MySQL的数据查询过程:

                                                            图2-2 MyFOX的数据查询过程
    在MyFOX的每一个节点中,存放着热节点和冷节点两种节点数据。顾名思义,热节点存放着最新的,被访问频率较高的数据;冷节点,存放着相对而来比较旧的,访问频率比较低的数据。而为了存储这两种节点数据,出于硬件条件和存储成本的考虑,你当然会考虑选择两种不同的硬盘,来存储这两种访问频率不同的节点数据。如下图所示:

                                                           图2-3 MyFOX节点结构
     “热节点”,选择每分钟15000转的SAS硬盘,按照一个节点两台机器来计算,单位数据的存储成本约为4.5W/TB。相对应地,“冷数据”我们选择了每分钟7500转的SATA硬盘,单碟上能够存放更多的数据,存储成本约为1.6W/TB。
Prom
出于文章篇幅的考虑,本文接下来不再过多阐述这个Prom了。如下面两幅图所示,他们分别表示的是Prom的存储结构以及Prom查询过程:

                                              图2-4 Prom的存储结构
 
                                                          图2-5 Prom查询过程
glide的技术架构
    
                                               图2-6 glider的技术架构
    在这一层-查询层中,淘宝主要是基于用中间层隔离前后端的理念而考虑。Glider这个中间层负责各个异构表之间的数据JOIN和UNION等计算,并且负责隔离前端产品和后端存储,提供统一的数据查询服务。
缓存
    除了起到隔离前后端以及异构“表”之间的数据整合的作用之外,glider的另外一个不容忽视的作用便是缓存管理。我们有一点须了解,在特定的时间段内,我们认为数据产品中的数据是只读的,这是利用缓存来提高性能的理论基础。
在上文图2-6中我们看到,glider中存在两层缓存,分别是基于各个异构“表”(datasource)的二级缓存和整合之后基于独立请求的一级缓存。除此之外,各个异构“表”内部可能还存在自己的缓存机制。

                                                           图2-7 缓存控制体系
    图2-7向我们展示了数据魔方在缓存控制方面的设计思路。用户的请求中一定是带了缓存控制的“命令”的,这包括URL中的query string,和HTTP头中的“If-None-Match”信息。并且,这个缓存控制“命令”一定会经过层层传递,最终传递到底层存储的异构“表”模块。
    缓存系统往往有两个问题需要面对和考虑:缓存穿透与失效时的雪崩效应。
缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。至于如何有效地解决缓存穿透问题,最常见的则是采用布隆过滤器(这个东西,在我的此篇文章中有介绍:),将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。
    而在数据魔方里,淘宝采用了一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
      2、缓存失效时的雪崩效应尽管对底层系统的冲击非常可怕。但遗憾的是,这个问题目前并没有很完美的解决方案。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上。
    在数据魔方中,淘宝设计的缓存过期机制理论上能够将各个客户端的数据失效时间均匀地分布在时间轴上,一定程度上能够避免缓存同时失效带来的雪崩效应。
本文参考:
基于云计算的海量数据存储模型,侯建等。
基于hadoop的海量日志数据处理,王小森
基于hadoop的大规模数据处理系统,王丽兵。
淘宝数据魔方技术架构解析,朋春。
Hadoop作业调优参数整理及原理,guili。
读者点评@xdylxdyl:
We want to count all the books in the library. You count up shelf #1, I count up shelf #2. That's map. The more people we get, the faster it goes. Now we get together and add our individual counts. That's reduce。
数据魔方里的缓存穿透,架构,空数据缓存这些和Hadoop一点关系都么有,如果是想讲一个Hadoop的具体应用的话,数据魔方这部分其实没讲清楚的。
感觉你是把两个东西混在一起了。不过这两个都是挺有价值的东西,或者说数据魔方的架构比Hadoop可能更重要一些,基本上大的互联网公司都会选择这么做。Null对象的缓存保留五分钟未必会有好的结果吧,如果Null对象不是特别大,数据的更新和插入不多也可以考虑实时维护。
Hadoop本身很笨重,不知道在数据魔方里是否是在扮演着实时数据处理的角色?还是只是在做线下的数据分析的?
结语:写文章是一种学习的过程。尊重他人劳动成果,转载请注明出处。谢谢。July、2011/8/20。完
复制表结构和数据SQL语句 database
1:复制表结构及数据到新表

select * into 目的数据库名.dbo.目的表名 from 原表名

select * into my0735home.dbo.infoMianTest from infoMian

2:备份表的一部分列(不写*而写出列的列表)

select 列名1,列名2,列名3 into 目的数据库名.dbo.目的表名 from 原表名

select id,title,mtype,stype,author,tel,nr into infoMianTest2 from infomian

3:备份表的一部分行(加WHERE条件)

select * into 目的数据库名.dbo.目的表名 from 原表名 where id<10

select * into infomiantest2 from infomian where id<10

4:备份表的一部分列(不写*而写出列的列表)和一部分行(加WHERE条件)

select 列名1,列名2,列名3 into 目的数据库名.dbo.目的表名 from 原表名 where  id<10

5:只复制表的结构:如:SELECT * INOT t1 FROM titles WHERE 1=2

6:查询结果来源于多个表:如:

SELECT title_id,title,pub_name INTO t3

FROM titles t INNER JOIN publishers p

ON t.pub_id=p.pub_id

转自:http://liujiassd.blog.163.com/blog/static/8311714320094283334854/
Hadoop源代码分析(完整版) hadoop http://wenku.baidu.com/view/ca9ee406cc17552707220861.html
http://wenku.baidu.com/view/7db2fcd276eeaeaad1f33055.html

http://wenku.baidu.com/view/ca9ee406cc17552707220861.html

http://download.csdn.net/detail/hippo111/3869033
eclipse启动服务报内存不足、报45s eclipse
内存不足:
   1、修改eclipse.ini文件
      openFile
      --launcher.XXMaxPermSize
      256M
      -vmargs
      -Dosgi.requiredJavaVersion=1.6
      -Xms40m
      -Xmx512m
      -XX:MaxPermSize=256m

   2、修改windows - preferences - installed jars -jvm 初始内存:
      -Xms128m  -Xmx512m -XX:PermSize=128M -XX:MaxPermSize=512M

   3、修改catalina.bat文件
      set JAVA_OPTS=-Xms512m -Xmx1024m -XX:PermSize=512M -XX:MaxNewSize=512m -XX:MaxPermSize=512m

启动服务45s退出:

   1、修改eclipse启动时间大于45s
     D:\Workspace1.5\.metadata\.plugins\org.eclipse.wst.server.core\servers.xml
     start-timeout="1000"
启动tomcat时找不到apr解决方法 service http://www.cnblogs.com/albert1017/archive/2012/08/14/2638226.html
2012-5-14 9:50:53 org.apache.catalina.core.AprLifecycleListener init
信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\ProgramWork\Java\jdk1.6.0_31\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;.;C:\ProgramWork\Java\jdk1.6.0_31\bin:C:\ProgramWork\Java\jdk1.6.0_31\jre\bin;C:\Windows\system32;................ (x86)\IDM Computer Solutions\UltraCompare\;C:\ProgramWork\GWT_library\App Engine_Home\google_appengine\;.
2012-5-14 9:50:53 org.apache.coyote.http11.Http11Protocol init
信息: Initializing Coyote HTTP/1.1 on http-8083
2012-5-14 9:50:53 org.apache.catalina.startup.Catalina load
信息: Initialization processed in 427 ms
2012-5-14 9:50:54 org.apache.catalina.core.StandardService start
信息: Starting service Catalina
2012-5-14 9:50:54 org.apache.catalina.core.StandardEngine start
信息: Starting Servlet Engine: Apache Tomcat/6.0.35
2012-5-14 9:50:54 org.apache.coyote.http11.Http11Protocol start
信息: Starting Coyote HTTP/1.1 on http-8083
2012-5-14 9:50:54 org.apache.jk.common.ChannelSocket init
信息: JK: ajp13 listening on /0.0.0.0:8029
2012-5-14 9:50:54 org.apache.jk.server.JkMain start
信息: Jk running ID=0 time=0/13  config=null
2012-5-14 9:50:54 org.apache.catalina.startup.Catalina start
信息: Server startup in 191 ms
网上找了一些:
出现这种情况是这表示没有找到APR
简要解决办法:去 http://tomcat.heanet.ie/native/ 下载编译好的tcnative-1.dll文件,目前最新为1.1.14,拷贝至jdk\bin下,再启动就可以成功加载APR了。 

这不是我要说的重点。 我按这操作,但是还是一样的错误。 
然后我找到 tomcat目录 通过 bat 目录启动, 结果启动正常,
我以为问题解决了,就又在Eclipse 启动 tomcat ,还是错误。。。
稍微思考下,就想通了。

只要将将tomcat/bin目录 下的 tcnative-1.dll 复制到 jdk/bin 目录下 
OK 解决了。当然 我这前提是tomcat 能通过starupt.bat运行。


Tomcat配置的文章 http://snowolf.iteye.com/blog/145770 
java的concurrent用法详解 java http://www.open-open.com/bbs/view/1320131360999
 我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便。而当针对高质量Java多线程并发程序设计时,为防止死蹦等现象的出现,比如使用java之前的wait()、notify()和synchronized等,每每需要考虑性能、死锁、公平性、资源管理以及如何避免线程安全性方面带来的危害等诸多因素,往往会采用一些较为复杂的安全策略,加重了程序员的开发负担.万幸的是,在JDK1.5出现之后,Sun大神(Doug Lea)终于为我们这些可怜的小程序员推出了java.util.concurrent工具包以简化并发完成。开发者们借助于此,将有效的减少竞争条件(race conditions)和死锁线程。concurrent包很好的解决了这些问题,为我们提供了更实用的并发程序模型。

Executor                  :具体Runnable任务的执行者。
ExecutorService           :一个线程池管理者,其实现类有多种,我会介绍一部分。我们能把Runnable,Callable提交到池中让其调度。
Semaphore                 :一个计数信号量
ReentrantLock             :一个可重入的互斥锁定 Lock,功能类似synchronized,但要强大的多。
Future                    :是与Runnable,Callable进行交互的接口,比如一个线程执行结束后取返回的结果等等,还提供了cancel终止线程。
BlockingQueue             :阻塞队列。
CompletionService         : ExecutorService的扩展,可以获得线程执行结果的
CountDownLatch            :一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。 
CyclicBarrier             :一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 
Future                    :Future 表示异步计算的结果。
ScheduledExecutorService :一个 ExecutorService,可安排在给定的延迟后运行或定期执行的命令。
接下来逐一介绍
Executors主要方法说明
newFixedThreadPool(固定大小线程池)
创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程(只有要请求的过来,就会在一个队列里等待执行)。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。
newCachedThreadPool(无界线程池,可以进行自动线程回收)
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。注意,可以使用 ThreadPoolExecutor 构造方法创建具有类似属性但细节不同(例如超时参数)的线程池。
newSingleThreadExecutor(单个后台线程)
创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
这些方法返回的都是ExecutorService对象,这个对象可以理解为就是一个线程池。
这个线程池的功能还是比较完善的。可以提交任务submit()可以结束线程池shutdown()。
01
import java.util.concurrent.ExecutorService;
02
import java.util.concurrent.Executors;
03
public class MyExecutor extends Thread {
04
private int index;
05
public MyExecutor(int i){
06
    this.index=i;
07
}
08
public void run(){
09
    try{
10
     System.out.println("["+this.index+"] start....");
11
     Thread.sleep((int)(Math.random()*1000));
12
     System.out.println("["+this.index+"] end.");
13
    }
14
    catch(Exception e){
15
     e.printStackTrace();
16
    }
17
}
18
public static void main(String args[]){
19
    ExecutorService service=Executors.newFixedThreadPool(4);
20
    for(int i=0;i<10;i++){
21
     service.execute(new MyExecutor(i));
22
     //service.submit(new MyExecutor(i));
23
    }
24
    System.out.println("submit finish");
25
    service.shutdown();
26
}
27
}

虽然打印了一些信息,但是看的不是非常清晰,这个线程池是如何工作的,我们来将休眠的时间调长10倍。
Thread.sleep((int)(Math.random()*10000));
再来看,会清楚看到只能执行4个线程。当执行完一个线程后,才会又执行一个新的线程,也就是说,我们将所有的线程提交后,线程池会等待执行完最后shutdown。我们也会发现,提交的线程被放到一个“无界队列里”。这是一个有序队列(BlockingQueue,这个下面会说到)。
另外它使用了Executors的静态函数生成一个固定的线程池,顾名思义,线程池的线程是不会释放的,即使它是Idle。
这就会产生性能问题,比如如果线程池的大小为200,当全部使用完毕后,所有的线程会继续留在池中,相应的内存和线程切换(while(true)+sleep循环)都会增加。
如果要避免这个问题,就必须直接使用ThreadPoolExecutor()来构造。可以像通用的线程池一样设置“最大线程数”、“最小线程数”和“空闲线程keepAlive的时间”。

这个就是线程池基本用法。
Semaphore
一个计数信号量。从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。
Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问:
这里是一个实际的情况,大家排队上厕所,厕所只有两个位置,来了10个人需要排队。
01
import java.util.concurrent.ExecutorService;
02
import java.util.concurrent.Executors;
03
import java.util.concurrent.Semaphore;
04
public class MySemaphore extends Thread {
05
Semaphore position;
06
private int id;
07
public MySemaphore(int i,Semaphore s){
08
    this.id=i;
09
    this.position=s;
10
}
11
public void run(){
12
    try{
13
     if(position.availablePermits()>0){
14
      System.out.println("顾客["+this.id+"]进入厕所,有空位");
15
     }
16
     else{
17
      System.out.println("顾客["+this.id+"]进入厕所,没空位,排队");
18
     }
19
     position.acquire();
20
     System.out.println("顾客["+this.id+"]获得坑位");
21
     Thread.sleep((int)(Math.random()*1000));
22
     System.out.println("顾客["+this.id+"]使用完毕");
23
     position.release();
24
    }
25
    catch(Exception e){
26
     e.printStackTrace();
27
    }
28
}
29
public static void main(String args[]){
30
    ExecutorService list=Executors.newCachedThreadPool();
31
    Semaphore position=new Semaphore(2);
32
    for(int i=0;i<10;i++){
33
     list.submit(new MySemaphore(i+1,position));
34
    }
35
    list.shutdown();
36
    position.acquireUninterruptibly(2);
37
    System.out.println("使用完毕,需要清扫了");
38
    position.release(2);
39
}
40
}
ReentrantLock
一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。
ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。
此类的构造方法接受一个可选的公平参数。
当设置为 true时,在多个线程的争用下,这些锁定倾向于将访问权授予等待时间最长的线程。否则此锁定将无法保证任何特定访问顺序。
与采用默认设置(使用不公平锁定)相比,使用公平锁定的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁定和保证锁定分配的均衡性时差异较小。不过要注意的是,公平锁定不能保证线程调度的公平性。因此,使用公平锁定的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁定时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁定是可用的,此方法就可以获得成功。
建议总是 立即实践,使用 try 块来调用 lock,在之前/之后的构造中,最典型的代码如下: 
01
class X {
02
    private final ReentrantLock lock = new ReentrantLock();
03
    // ...
04
    public void m() {
05
      lock.lock(); // block until condition holds
06
      try {
07
        // ... method body
08
      } finally {
09
        lock.unlock()
10
      }
11
    }
12
}
我的例子:
01
import java.util.concurrent.ExecutorService;
02
import java.util.concurrent.Executors;
03
import java.util.concurrent.locks.ReentrantLock;
04
public class MyReentrantLock extends Thread{
05
TestReentrantLock lock;
06
private int id;
07
public MyReentrantLock(int i,TestReentrantLock test){
08
    this.id=i;
09
    this.lock=test;
10
}
11
public void run(){
12
    lock.print(id);
13
}
14
public static void main(String args[]){
15
    ExecutorService service=Executors.newCachedThreadPool();
16
    TestReentrantLock lock=new TestReentrantLock();
17
    for(int i=0;i<10;i++){
18
     service.submit(new MyReentrantLock(i,lock));
19
    }
20
    service.shutdown();
21
}
22
}
23
class TestReentrantLock{
24
private ReentrantLock lock=new ReentrantLock();
25
public void print(int str){
26
    try{
27
     lock.lock();
28
     System.out.println(str+"获得");
29
     Thread.sleep((int)(Math.random()*1000));
30
    }
31
    catch(Exception e){
32
     e.printStackTrace();
33
    }
34
    finally{
35
     System.out.println(str+"释放");
36
     lock.unlock();
37
    }
38
}
39
}
BlockingQueue
支持两个附加操作的 Queue,这两个操作是:检索元素时等待队列变为非空,以及存储元素时等待空间变得可用。
BlockingQueue 不接受 null 元素。试图 add、put 或 offer 一个 null 元素时,某些实现会抛出 NullPointerException。null 被用作指示 poll 操作失败的警戒值。
BlockingQueue 可以是限定容量的。它在任意给定时间都可以有一个 remainingCapacity,超出此容量,便无法无阻塞地 put 额外的元素。
没有任何内部容量约束的 BlockingQueue 总是报告 Integer.MAX_VALUE 的剩余容量。
BlockingQueue 实现主要用于生产者-使用者队列,但它另外还支持 Collection 接口。因此,举例来说,使用 remove(x) 从队列中移除任意一个元素是有可能的。
然而,这种操作通常不 会有效执行,只能有计划地偶尔使用,比如在取消排队信息时。
BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁定或其他形式的并发控制来自动达到它们的目的。
然而,大量的 Collection 操作(addAll、containsAll、retainAll 和 removeAll)没有 必要自动执行,除非在实现中特别说明。
因此,举例来说,在只添加了 c 中的一些元素后,addAll(c) 有可能失败(抛出一个异常)。
BlockingQueue 实质上不 支持使用任何一种“close”或“shutdown”操作来指示不再添加任何项。
这种功能的需求和使用有依赖于实现的倾向。例如,一种常用的策略是:对于生产者,插入特殊的 end-of-stream 或 poison 对象,并根据使用者获取这些对象的时间来对它们进行解释。
下面的例子演示了这个阻塞队列的基本功能。
01
import java.util.concurrent.BlockingQueue;
02
import java.util.concurrent.ExecutorService;
03
import java.util.concurrent.Executors;
04
import java.util.concurrent.LinkedBlockingQueue;
05
public class MyBlockingQueue extends Thread {
06
public static BlockingQueue<String> queue = new LinkedBlockingQueue<String>(3);
07
private int index;
08
public MyBlockingQueue(int i) {
09
   this.index = i;
10
}
11
public void run() {
12
   try {
13
    queue.put(String.valueOf(this.index));
14
    System.out.println("{" + this.index + "} in queue!");
15
   } catch (Exception e) {
16
    e.printStackTrace();
17
   }
18
}
19
public static void main(String args[]) {
20
   ExecutorService service = Executors.newCachedThreadPool();
21
   for (int i = 0; i < 10; i++) {
22
    service.submit(new MyBlockingQueue(i));
23
   }
24
   Thread thread = new Thread() {
25
    public void run() {
26
     try {
27
      while (true) {
28
       Thread.sleep((int) (Math.random() * 1000));
29
       if(MyBlockingQueue.queue.isEmpty())
30
        break;
31
       String str = MyBlockingQueue.queue.take();
32
       System.out.println(str + " has take!");
33
      }
34
     } catch (Exception e) {
35
      e.printStackTrace();
36
     }
37
    }
38
   };
39
   service.submit(thread);
40
   service.shutdown();
41
}
42
}
---------------------执行结果-----------------
{0} in queue!
{1} in queue!
{2} in queue!
{3} in queue!
0 has take!
{4} in queue!
1 has take!
{6} in queue!
2 has take!
{7} in queue!
3 has take!
{8} in queue!
4 has take!
{5} in queue!
6 has take!
{9} in queue!
7 has take!
8 has take!
5 has take!
9 has take!
-----------------------------------------

CompletionService
将生产新的异步任务与使用已完成任务的结果分离开来的服务。生产者 submit 执行的任务。使用者 take 已完成的任务,
并按照完成这些任务的顺序处理它们的结果。例如,CompletionService 可以用来管理异步 IO ,执行读操作的任务作为程序或系统的一部分提交,
然后,当完成读操作时,会在程序的不同部分执行其他操作,执行操作的顺序可能与所请求的顺序不同。
通常,CompletionService 依赖于一个单独的 Executor 来实际执行任务,在这种情况下,
CompletionService 只管理一个内部完成队列。ExecutorCompletionService 类提供了此方法的一个实现。

01
import java.util.concurrent.Callable;
02
import java.util.concurrent.CompletionService;
03
import java.util.concurrent.ExecutorCompletionService;
04
import java.util.concurrent.ExecutorService;
05
import java.util.concurrent.Executors;
06
public class MyCompletionService implements Callable<String> {
07
private int id;
08
 
09
public MyCompletionService(int i){
10
   this.id=i;
11
}
12
public static void main(String[] args) throws Exception{
13
   ExecutorService service=Executors.newCachedThreadPool();
14
   CompletionService<String> completion=new ExecutorCompletionService<String>(service);
15
   for(int i=0;i<10;i++){
16
    completion.submit(new MyCompletionService(i));
17
   }
18
   for(int i=0;i<10;i++){
19
    System.out.println(completion.take().get());
20
   }
21
   service.shutdown();
22
}
23
public String call() throws Exception {
24
   Integer time=(int)(Math.random()*1000);
25
   try{
26
    System.out.println(this.id+" start");
27
    Thread.sleep(time);
28
    System.out.println(this.id+" end");
29
   }
30
   catch(Exception e){
31
    e.printStackTrace();
32
   }
33
   return this.id+":"+time;
34
}
35
}

CountDownLatch

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。
之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。
CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,
或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。
用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。
CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,
而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。 
一下的例子是别人写的,非常形象。
01
import java.util.concurrent.CountDownLatch;
02
import java.util.concurrent.ExecutorService;
03
import java.util.concurrent.Executors;
04
public class TestCountDownLatch {
05
public static void main(String[] args) throws InterruptedException {
06
   // 开始的倒数锁
07
   final CountDownLatch begin = new CountDownLatch(1);
08
   // 结束的倒数锁
09
   final CountDownLatch end = new CountDownLatch(10);
10
   // 十名选手
11
   final ExecutorService exec = Executors.newFixedThreadPool(10);
12
   
13
   for (int index = 0; index < 10; index++) {
14
    final int NO = index + 1;
15
    Runnable run = new Runnable() {
16
     public void run() {
17
      try {
18
       begin.await();//一直阻塞
19
       Thread.sleep((long) (Math.random() * 10000));
20
       System.out.println("No." + NO + " arrived");
21
      } catch (InterruptedException e) {
22
      } finally {
23
       end.countDown();
24
      }
25
     }
26
    };
27
    exec.submit(run);
28
   }
29
   System.out.println("Game Start");
30
   begin.countDown();
31
   end.await();
32
   System.out.println("Game Over");
33
   exec.shutdown();
34
}
35
}

CountDownLatch最重要的方法是countDown()和await(),前者主要是倒数一次,后者是等待倒数到0,如果没有到达0,就只有阻塞等待了。

CyclicBarrier
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。
在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),
该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
示例用法:下面是一个在并行分解设计中使用 barrier 的例子,很经典的旅行团例子:
01
import java.text.SimpleDateFormat;
02
import java.util.Date;
03
import java.util.concurrent.BrokenBarrierException;
04
import java.util.concurrent.CyclicBarrier;
05
import java.util.concurrent.ExecutorService;
06
import java.util.concurrent.Executors;
07
public class TestCyclicBarrier {
08
  // 徒步需要的时间: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhan
09
  private static int[] timeWalk = { 5, 8, 15, 15, 10 };
10
  // 自驾游
11
  private static int[] timeSelf = { 1, 3, 4, 4, 5 };
12
  // 旅游大巴
13
  private static int[] timeBus = { 2, 4, 6, 6, 7 };
14
   
15
  static String now() {
16
     SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
17
     return sdf.format(new Date()) + ": ";
18
  }
19
  static class Tour implements Runnable {
20
     private int[] times;
21
     private CyclicBarrier barrier;
22
     private String tourName;
23
     public Tour(CyclicBarrier barrier, String tourName, int[] times) {
24
       this.times = times;
25
       this.tourName = tourName;
26
       this.barrier = barrier;
27
     }
28
     public void run() {
29
       try {
30
         Thread.sleep(times[0] * 1000);
31
         System.out.println(now() + tourName + " Reached Shenzhen");
32
         barrier.await();
33
         Thread.sleep(times[1] * 1000);
34
         System.out.println(now() + tourName + " Reached Guangzhou");
35
         barrier.await();
36
         Thread.sleep(times[2] * 1000);
37
         System.out.println(now() + tourName + " Reached Shaoguan");
38
         barrier.await();
39
         Thread.sleep(times[3] * 1000);
40
         System.out.println(now() + tourName + " Reached Changsha");
41
         barrier.await();
42
         Thread.sleep(times[4] * 1000);
43
         System.out.println(now() + tourName + " Reached Wuhan");
44
         barrier.await();
45
       } catch (InterruptedException e) {
46
       } catch (BrokenBarrierException e) {
47
       }
48
     }
49
  }
50
  public static void main(String[] args) {
51
     // 三个旅行团
52
     CyclicBarrier barrier = new CyclicBarrier(3);
53
     ExecutorService exec = Executors.newFixedThreadPool(3);
54
     exec.submit(new Tour(barrier, "WalkTour", timeWalk));
55
     exec.submit(new Tour(barrier, "SelfTour", timeSelf));
56
//当我们把下面的这段代码注释后,会发现,程序阻塞了,无法继续运行下去。
57
     exec.submit(new Tour(barrier, "BusTour", timeBus));
58
     exec.shutdown();
59
  }
60
}

CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。
Future
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
计算完成后只能使用 get 方法来检索结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。
还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。
如果为了可取消性而使用 Future但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为基础任务的结果。
这个我们在前面CompletionService已经看到了,这个Future的功能,而且这个可以在提交线程的时候被指定为一个返回对象的。

ScheduledExecutorService
一个 ExecutorService,可安排在给定的延迟后运行或定期执行的命令。
schedule 方法使用各种延迟创建任务,并返回一个可用于取消或检查执行的任务对象。scheduleAtFixedRate 和 scheduleWithFixedDelay 方法创建并执行某些在取消前一直定期运行的任务。
用 Executor.execute(java.lang.Runnable) 和 ExecutorService 的 submit 方法所提交的命令,通过所请求的 0 延迟进行安排。
schedule 方法中允许出现 0 和负数延迟(但不是周期),并将这些视为一种立即执行的请求。
所有的 schedule 方法都接受相对 延迟和周期作为参数,而不是绝对的时间或日期。将以 Date 所表示的绝对时间转换成要求的形式很容易。
例如,要安排在某个以后的日期运行,可以使用:schedule(task, date.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS)。
但是要注意,由于网络时间同步协议、时钟漂移或其他因素的存在,因此相对延迟的期满日期不必与启用任务的当前 Date 相符。
Executors 类为此包中所提供的 ScheduledExecutorService 实现提供了便捷的工厂方法。
一下的例子也是网上比较流行的。
view sourceprint?
01
import static java.util.concurrent.TimeUnit.SECONDS;
02
import java.util.Date;
03
import java.util.concurrent.Executors;
04
import java.util.concurrent.ScheduledExecutorService;
05
import java.util.concurrent.ScheduledFuture;
06
public class TestScheduledThread {
07
public static void main(String[] args) {
08
   final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
09
   final Runnable beeper = new Runnable() {
10
    int count = 0;
11
    public void run() {
12
     System.out.println(new Date() + " beep " + (++count));
13
    }
14
   };
15
   // 1秒钟后运行,并每隔2秒运行一次
16
   final ScheduledFuture beeperHandle = scheduler.scheduleAtFixedRate(beeper, 1, 2, SECONDS);
17
   // 2秒钟后运行,并每次在上次任务运行完后等待5秒后重新运行
18
   final ScheduledFuture beeperHandle2 = scheduler.scheduleWithFixedDelay(beeper, 2, 5, SECONDS);
19
   // 30秒后结束关闭任务,并且关闭Scheduler
20
   scheduler.schedule(new Runnable() {
21
    public void run() {
22
     beeperHandle.cancel(true);
23
     beeperHandle2.cancel(true);
24
     scheduler.shutdown();
25
    }
26
   }, 30, SECONDS);
27
}
28
}
这样我们就把concurrent包下比较重要的功能都已经总结完了,希望对我们理解能有帮助。
UML各类图 uml
原文:http://www.cnblogs.com/way-peng/archive/2012/06/11/2544932.html
      http://www.cnblogs.com/wangkangluo1/archive/2013/02/21/2920606.html

一、UML是什么?UML有什么用?

二、UML的历史

三、UML的上层结构(Superstructure)

四、UML建模工具

五、UML的图(重点)

   1、用例图(use case diagram)

   2、活动图(activity diagram)

   3、静态结构图

   4、顺序图(Sequence Diagram)

   5、交互纵览图(Interaction Overview Diagram)

   6、通信图(Communication Diagram)

   7、时间图(Timing Diagram)

   8、状态机图(State Machine Diagram)

   9、构件图(Component Diagram)

   10、部署图(Deployment Diagram)

一、UML是什么?UML有什么用?

UML是什么?

Unified Modeling Language(统一建模语言)是对象管理组织(OMG)制定的一个通用的、可视化的建模语言标准,可以用来可视化(visualize)、描述(specify)、构造(construct)和文档化(document)软件密集型系统的各种工件(artifacts,又译制品)

UML是一种标准的图形化建模语言,是面向对象分析与设计的标准表示,它:

星星不是一种可视化的程序设计语言,而是一种可视化的建模语言(用于分析设计)

星星不是工具或知识库的规格说明,而是一种建模语言规格说明,是一种表示的标准

星星不是过程,也不是方法,但允许任何一种过程和方法使用它

UML有什么用?

软件开发系统规模比较复杂时,需要用图形抽象地表达复杂概念,增强设计的灵活性、可读性和可理解性,以便暴露深层次的设计问题,降低开发风险。有必要采用一套通用的图形语言和符号体系描述组织的业务流程和软件需求,促进业务人员、开发人员之间一致、高效地交流。

二、UML的历史

UML发展背景:

P. Coad和E.Yourdon提出OOA和OOD

G. Booch提出面向对象开发方法

Jacobson提出OOSE

Rumbaugh提出的OMT

……

UML的出现结束了这场方法学战争

UML发展历程:

image

三、UML的上层结构(Superstructure)

至于UML底层的基础结构(Infrastructure),软件开发工程师们没必要了解,只需要懂得上层结构就行了。

image

构造块(building blocks)	通用机制(common mechanisms)	构架(architecture)
星星事物(things)

    结构、行为、分组、注释

星星关系(relationships)

   依赖、关联、泛化、实现

星星图(diagram)

   静态(7种):类图、对象图、构件图、部署图、包图、组合结构图、外廓图

   动态(7种):顺序图、通信图、时间图、交互纵览图、活动图、状态机图、用例图

星星规格说明(Specifications)

文本维度的模型描述

星星修饰(Adornments)

描述建模元素的细节信息

星星通用划分(Common Divisions)

建模时对事物的划分方法

星星扩展机制(Extensibility Mechanisms)

构造型、约束、标记值

4+1视图 
UML中的视图包括用例视图(Use Case View)、逻辑视图(Logical View)、实现视图(Implementation View)、进程视图(Process View)、部署视图(Deployment View)等,这5个视图被称作”4+1”视图.image
四、UML建模工具

比较流行的有Rational Rose ,Microsoft Visio、Enterprise Architect 、Visual UML等。我现在使用的UML建模工具是Enterprise Architect 8.0,推荐使用这款,比较好用。

五、UML的图(重点)

image

1、用例图(use case diagram)

灯泡用例图(Use Case Diagram)是被称为参与者(Actor)的外部用户所能观察到的系统功能的模型图

     列出系统中的用例和参与者

     显示哪个参与者参与了哪个用例的执行

灯泡核心概念

    用例:系统中的一个功能单元,可以被描述为参与者与系统之间的一次交互作用

    参与者、参与者泛化

    用例与参与者之间的关系:关联

    用例之间关系:扩展、包括、泛化

灯泡推荐使用场合

    业务建模、需求获取、定义

某图书馆管理系统:

是一个基于Web的计算机应用系统;

读者可以查询图书信息以及借阅信息;

读者可以通过系统预约所需的图书;

图书馆工作人员利用该系统完成读者的借书、还书业务;

图书馆工作人员可以对图书信息、读者信息等进行维护;

对于到期的图书,系统会自动向读者发送催还信息;

管理员会定期进行系统维护;

……

image 
UC01:“借书”用例文档

用例名称:借书

用例标识:UC01

涉及的参与者:工作人员

涉及的用例:无

描述:工作人员利用该用例为读者完成借书过程

前置条件:工作人员必须登录到当前系统

涉众利益:

     读者:能够方便的找到并借出所需的图书

     工作人员:能够快速并准确的完成借书工作

基本事件流:工作人员帮助读者借阅图书

   1.用例起始于读者带着所要借的图书来到借阅前台;

   2.工作人员录入读者信息;

   3.工作人员逐一录入所有的图书信息:

   * 3.1 工作人员录入一本图书信息;

   * 3.2 系统确认该读者可以借阅当前图书;

   4.工作人员确认本次借阅信息;

   5.系统记录本次借阅情况。

后置条件:系统将读者借阅信息正确地记录到数据库中

备选事件流

   2a. 读者身份不合法

   2b. 读者存在欠费信息,不允许借书

   3.2a. 该读者不允许借阅当前图书

字段列表:

   5. 借阅信息主要包括:读者图书证号、图书编号、借阅日期(默认为当天日期)、借阅天数以及归还日期。

业务规则

   3.2 系统根据当前读者的借阅规则来判断是否可以借阅图书;而借阅规则取决于读者的类型(如本科生、研究生、老师等)和图书的类型(如科技类、文学类、新书等),并可动态配置

非功能需求:无

设计约束:无

部署约束:无

未解决的问题

   2b. 读者存在多少欠费记录时,才不允许借书?

   3.2 借阅规则的具体配置情况需和用户进一步讨论?

2、活动图(activity diagram)

灯泡活动图(Activity Diagram)

通过动作来组织,主要用于描述某一方法、机制或用例的内部行为

灯泡核心概念

状态、活动、组合活动、对象

转移、分支

并发、同步

泳道

灯泡推荐使用场合

业务建模、需求、类设计

image
 

3、静态结构图

类图(Class Diagram)

是软件的蓝图,详细描述了系统内各个对象的相关的类,以及这些类之间的静态关系。

核心概念:类、接口、依赖、关联、泛化、实现

类图展示实体类的静态关系:

image
对象图(Object Diagram)

表示在某一时刻类的对象静态结构和行为。

核心概念:对象、链接、多重性

对象图展示我当前借书情况:

image
包图(Package Diagram)

展现有模型本身分解而成的组织单元(包)以及它们的依赖关系。

核心概念:包(、框架、层、子系统) 、依赖

包图展示系统分层结构:

image
组合结构图(Composite Structure Diagram)

描述系统中某一部分(组合结构)的内部结构,包括该部分与系统其它部分的交互点。 
核心概念:组合结构、部件、端口、协议

组合结构图展示借书内部结构:

image
 

4、顺序图(Sequence Diagram)

灯泡顺序图(Sequence Diagram)

用于显示对象间的交互活动

关注对象之间消息传送的时间顺序

灯泡核心概念

对象、生命线、激活、交互、消息

交互帧(Interaction Frame)

灯泡推荐使用场合

用例分析、用例设计

“借书”用例实现的顺序图image
5、交互纵览图(Interaction Overview Diagram)

灯泡交互纵览图(Interaction Overview Diagram)

活动图和顺序图的混合物

直观地表达一组相关顺序图之间的流转逻辑

灯泡核心概念

交互帧

分支、转移

灯泡推荐使用场合

用例分析、用例设计

交互纵览图组织多个顺序图image
6、通信图(Communication Diagram)

灯泡通信图(Communication Diagram)

UML 1.x中称为协作图(Collaboration Diagram)

表示一组对象间关系以及交互活动

灯泡核心概念

对象、协作角色

协作、交互、消息

灯泡推荐使用场合

用例分析、用例设计

“借书”用例实现的通信图 
image
7、时间图(Timing Diagram)

灯泡时间图(Timing Diagram)

一种交互图,展现消息跨越不同对象或角色的实际时间信息;

具体描述单个或多个对象状态变化的时间点以及维持特定状态的时间段;

顺序图是表示交互的主要手段,可以在顺序图中增加时间约束来表明对象状态变化的时间点以及维持特定状态的时间段。

灯泡核心概念

时间约束、持续时间约束、生命线

状态、条件、事件

“打电话”顺序图的时间约束 
image
利用时间图描述时间约束 
image
8、状态机图(State Machine Diagram)

灯泡状态机图(State Machine Diagram)

UML1.x为状态图(Statechart Diagram)

利用状态和事件描述对象本身的行为

灯泡主要概念

状态、初态、终态、复合状态

事件、转移、动作

并发

灯泡推荐使用场合

类设计

“图书”类的状态机图 
image
9、构件图(Component Diagram)

灯泡构件图(Component Diagram)

封装类为构件

描述在系统实现环境中的软件构件和之间的关系

灯泡主要概念

构件、工件、接口(所供接口、所需接口)

依赖、实现

灯泡推荐使用场合

系统设计、实现、部署

构件图描述类的实现环境 
image
10、部署图(Deployment Diagram)

 

灯泡部署图(Deployment Diagram)

描述系统所需的硬件构件的物理部署

灯泡主要概念

节点、构件、位置

连接、依赖

灯泡推荐使用场合

系统设计、实施、部署

部署图描述系统部署情况 
image
JAVA浏览器 jndi
CDSN:http://blog.csdn.net/lxf9601/article/details/6967167


使用VC,VB或者C#的开发者们对于在程序里面嵌入一个网页来说,那真是小事一桩。但是在JAVA里面,却几乎是不可能实现的任务。JEditorPane虽然说可以打开网页,但是它那解析速度以及解析质量,对于今天日益复杂的网页内容来说,就像没有一样。今天我们就使用一个开源的组件(jdic)来实现在JAVA程序里面嵌入网页的效率,运行界面如下:



下面言归正转吧,我们来介绍一下这个开源的组件,它的名字叫JDIC(JDesktop Integration Components),网址为:https://jdic.dev.java.net/,它提供了一种访问桌面组件的API,其中JDK6.0就采纳了其中了一些,比如系统栏图标的SystemTray和SystemIcon,还有代表桌面的Desktop等等,可见这个API是挺不错的。由于网页浏览器的特殊性,标准的JDK并没有把它加入进来,但是我们一样可以下载它来使用这个功能。明显地,这个功能是用本地方法实现的,所以下载完以后,把jdic.dll放到我们的path目录中,比如system32文件夹下面,然后我们就可以使用它的功能从而增加我们的JAVA程序了。

上面的例子代码如下:
/*
 * Test1.java
 *
 * Created on 2007-10-2, 17:29:30
 *
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package test2;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.jdesktop.jdic.browser.IWebBrowser;
import org.jdesktop.jdic.browser.WebBrowser;
import org.jdesktop.jdic.browser.WebBrowserEvent;
import org.jdesktop.jdic.browser.WebBrowserListenerAdapter;

/**
 *
 * @author hadeslee
 */
public class Test1 extends JPanel implements ActionListener {

    private JTextField input;
    private JButton go;
    private IWebBrowser web;

    public Test1() {
        super(new BorderLayout());
        initWindow();
    }

    private void initWindow() {
        try {
            web = new WebBrowser();
            web.addWebBrowserListener(new MyListener());
            go = new JButton("转到");
            input = new JTextField();
            JPanel up = new JPanel(new BorderLayout());
            up.add(input, BorderLayout.CENTER);
            up.add(go, BorderLayout.EAST);
            this.add(up, BorderLayout.NORTH);
            this.add(web.asComponent(), BorderLayout.CENTER);
            input.addActionListener(this);
            go.addActionListener(this);
        } catch (Exception ex) {
            Logger.getLogger(Test1.class.getName()).log(Level.SEVERE, null, ex);
        }
        JFrame jf = new JFrame("JAVA浏览器");
        jf.add(this, BorderLayout.CENTER);
        jf.setSize(500, 300);
        jf.setLocationRelativeTo(null);
        jf.setVisible(true);
        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    public void actionPerformed(ActionEvent ae) {
        doOpen();
    }

    private void doOpen() {
        try {
            String text = input.getText();
            if (text == null || text.equals("")) {
                return;
            }
            if (!text.toLowerCase().startsWith("http://")) {
                text = "http://" + text;
            }
            web.setURL(new URL(text));
        } catch (MalformedURLException ex) {
            Logger.getLogger(Test1.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public static void main(String[] args) {
        new Test1();
    }

    private class MyListener extends WebBrowserListenerAdapter {

        private MyListener() {
        }

        @Override
        public void documentCompleted(WebBrowserEvent arg0) {
            System.out.println("文档下载完。。。");
            web.executeScript("alert('文档下载完毕!')");
//            web.setContent("<html><H1>Hello world!!<H1>" +
//                    "<a href=http://www.google.cn>点我</a></html>");
//            web.removeWebBrowserListener(this);
        }
    }
}

它比一般的别的实现好的地方就是,它可以很完全地和JAVA代码进行交互,包括浏览器事件的监听,浏览器内容的获取,以及自己调用浏览器来执行一段javasript,这些都是很强大并且很实用的功能。
怎么样,这下满足了一下我们把网页嵌入到JAVA程序中的愿望了吧。
Java相对路径/绝对路径总结(转) java http://www.iteye.com/topic/117978/
1.基本概念的理解

绝对路径:绝对路径就是你的主页上的文件或目录在硬盘上真正的路径,(URL和物理路径)例如:
C:xyz est.txt 代表了test.txt文件的绝对路径。http://www.sun.com/index.htm也代表了一个URL绝对路径。

相对路径:相对与某个基准目录的路径。包含Web的相对路径(HTML中的相对目录),例如:在
Servlet中,"/"代表Web应用的跟目录。和物理路径的相对表示。例如:"./" 代表当前目录,"../"代表上级目录。这种类似的表示,也是属于相对路径。
另外关于URI,URL,URN等内容,请参考RFC相关文档标准。

RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax,
(http://www.ietf.org/rfc/rfc2396.txt)


2.关于JSP/Servlet中的相对路径和绝对路径。

2.1服务器端的地址

服务器端的相对地址指的是相对于你的web应用的地址,这个地址是在服务器端解析的(不同于html和javascript中的相对地址,他们是由客户端浏览器解析的)也就是说这时候在jsp和servlet中的相对地址应该是相对于你的web应用,即相对于http: //192.168.0.1/webapp/的。

其用到的地方有:
forward:servlet中的request.getRequestDispatcher(address);这个address是在服务器端解析的,所以,你要forward到a.jsp应该这么写:request.getRequestDispatcher(“/user/a.jsp”)这个/ 相对于当前的web应用webapp,其绝对地址就是:http://192.168.0.1/webapp/user/a.jsp。 sendRedirect:在jsp中<%response.sendRedirect("/rtccp/user/a.jsp");%>

2.22、客户端的地址

所有的html页面中的相对地址都是相对于服务器根目录(http://192.168.0.1/)的,而不是(跟目录下的该Web应用的目录) http://192.168.0.1/webapp/的。 Html中的form表单的action属性的地址应该是相对于服务器根目录(http://192.168.0.1/)的,所以,如果提交到a.jsp 为:action="/webapp/user/a.jsp"或action="<%=request.getContextPath()% >"/user/a.jsp;
提交到servlet为actiom="/webapp/handleservlet" Javascript也是在客户端解析的,所以其相对路径和form表单一样。


因此,一般情况下,在JSP/HTML页面等引用的CSS,Javascript.Action等属性前面最好都加上
<%=request.getContextPath()%>,以确保所引用的文件都属于Web应用中的目录。另外,应该尽量避免使用类似".","./","../../"等类似的相对该文件位置的相对路径,这样当文件移动时,很容易出问题。


3. JSP/Servlet中获得当前应用的相对路径和绝对路径

3.1 JSP中获得当前应用的相对路径和绝对路径
根目录所对应的绝对路径:request.getRequestURI()
文件的绝对路径  :application.getRealPath(request.getRequestURI());
当前web应用的绝对路径 :application.getRealPath("/");
取得请求文件的上层目录:new File(application.getRealPath(request.getRequestURI())).getParent()

3.2 Servlet中获得当前应用的相对路径和绝对路径
根目录所对应的绝对路径:request.getServletPath();
文件的绝对路径 :request.getSession().getServletContext().getRealPath
(request.getRequestURI())
当前web应用的绝对路径 :servletConfig.getServletContext().getRealPath("/");
(ServletContext对象获得几种方式:
javax.servlet.http.HttpSession.getServletContext()
javax.servlet.jsp.PageContext.getServletContext()
javax.servlet.ServletConfig.getServletContext()
)

4.java 的Class中获得相对路径,绝对路径的方法

4.1单独的Java类中获得绝对路径
根据java.io.File的Doc文挡,可知:
默认情况下new File("/")代表的目录为:System.getProperty("user.dir")。
一下程序获得执行类的当前路径 
 
package org.cheng.file;  
  
import java.io.File;  
  
public class FileTest {  
    public static void main(String[] args) throws Exception {  
        System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));  
  
        System.out.println(FileTest.class.getClassLoader().getResource(""));  
  
        System.out.println(ClassLoader.getSystemResource(""));  
        System.out.println(FileTest.class.getResource(""));  
        System.out.println(FileTest.class.getResource("/"));
        //Class文件所在路径
        System.out.println(new File("/").getAbsolutePath());  
        System.out.println(System.getProperty("user.dir"));  
    }  
}  

4.2服务器中的Java类获得当前路径(来自网络)

(1).Weblogic

WebApplication的系统文件根目录是你的weblogic安装所在根目录。
例如:如果你的weblogic安装在c:eaweblogic700.....
那么,你的文件根路径就是c:.
所以,有两种方式能够让你访问你的服务器端的文件:
a.使用绝对路径:
比如将你的参数文件放在c:yourconfigyourconf.properties,
直接使用 new FileInputStream("yourconfig/yourconf.properties");
b.使用相对路径:
相对路径的根目录就是你的webapplication的根路径,即WEB-INF的上一级目录,将你的参数文件放

在yourwebappyourconfigyourconf.properties,
这样使用:
new FileInputStream("./yourconfig/yourconf.properties");
这两种方式均可,自己选择。

(2).Tomcat

在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin

(3).Resin

不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET
的路径为根.比如用新建文件法测试File f = new File("a.htm");
这个a.htm在resin的安装目录下

(4).如何读相对路径哪?

在Java文件中getResource或getResourceAsStream均可

例:getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web

发布根路径下WEB-INF/classes

默认使用该方法的路径是:WEB-INF/classes。已经在Tomcat中测试。

5.读取文件时的相对路径,避免硬编码和绝对路径的使用。(来自网络)
5.1 采用Spring的DI机制获得文件,避免硬编码。
参考下面的连接内容:
http://www.javajia.net/viewtopic.php?p=90213&
5.2 配置文件的读取
参考下面的连接内容:
http://dev.csdn.net/develop/article/39/39681.shtm

5.3 通过虚拟路径或相对路径读取一个xml文件,避免硬编码

参考下面的连接内容:
http://club.gamvan.com/club/clubPage.jsp?iPage=1&tID=10708&ccID=8

6.Java中文件的常用操作(复制,移动,删除,创建等)(来自网络)
常用 java File 操作类
http://www.easydone.cn/014/200604022353065155.htm

Java文件操作大全(JSP中)
http://www.pconline.com.cn/pcedu/empolder/gj/java/0502/559401.html

java文件操作详解(Java中文网)
http://www.51cto.com/html/2005/1108/10947.htm

JAVA 如何创建删除修改复制目录及文件
http://www.gamvan.com/developer/java/2005/2/264.html

总结:
通过上面内容的使用,可以解决在Web应用服务器端,移动文件,查找文件,复制
删除文件等操作,同时对服务器的相对地址,绝对地址概念更加清晰。
建议参考URI,的RFC标准文挡。同时对Java.io.File. Java.net.URI.等内容了解透彻
对其他方面的理解可以更加深入和透彻。
java获得当前服务器信息 java
mport java.util.Properties;

public class Test{

 public static void main (String args[]){
  Properties props=System.getProperties(); //系统属性

     System.out.println("Java的运行环境版本:"+props.getProperty("java.version"));
     System.out.println("Java的运行环境供应商:"+props.getProperty("java.vendor"));
     System.out.println("Java供应商的URL:"+props.getProperty("java.vendor.url"));
     System.out.println("Java的安装路径:"+props.getProperty("java.home"));
     System.out.println("Java的虚拟机规范版本:"+props.getProperty("java.vm.specification.version"));
     System.out.println("Java的虚拟机规范供应商:"+props.getProperty("java.vm.specification.vendor"));
     System.out.println("Java的虚拟机规范名称:"+props.getProperty("java.vm.specification.name"));
     System.out.println("Java的虚拟机实现版本:"+props.getProperty("java.vm.version"));
     System.out.println("Java的虚拟机实现供应商:"+props.getProperty("java.vm.vendor"));
     System.out.println("Java的虚拟机实现名称:"+props.getProperty("java.vm.name"));
     System.out.println("Java运行时环境规范版本:"+props.getProperty("java.specification.version"));
     System.out.println("Java运行时环境规范供应商:"+props.getProperty("java.specification.vender"));
     System.out.println("Java运行时环境规范名称:"+props.getProperty("java.specification.name"));
     System.out.println("Java的类格式版本号:"+props.getProperty("java.class.version"));
     System.out.println("Java的类路径:"+props.getProperty("java.class.path"));
     System.out.println("加载库时搜索的路径列表:"+props.getProperty("java.library.path"));
     System.out.println("默认的临时文件路径:"+props.getProperty("java.io.tmpdir"));
     System.out.println("一个或多个扩展目录的路径:"+props.getProperty("java.ext.dirs"));
     System.out.println("操作系统的名称:"+props.getProperty("os.name"));
     System.out.println("操作系统的构架:"+props.getProperty("os.arch"));
     System.out.println("操作系统的版本:"+props.getProperty("os.version"));
     System.out.println("文件分隔符:"+props.getProperty("file.separator"));   //在 unix 系统中是”/”
     System.out.println("路径分隔符:"+props.getProperty("path.separator"));   //在 unix 系统中是”:”
     System.out.println("行分隔符:"+props.getProperty("line.separator"));   //在 unix 系统中是”/n”
     System.out.println("用户的账户名称:"+props.getProperty("user.name"));
     System.out.println("用户的主目录:"+props.getProperty("user.home"));
     System.out.println("用户的当前工作目录:"+props.getProperty("user.dir"));

}

}
NFC与RFID nfc
NFC是在RFID的基础上发展而来,NFC从本质上与RFID没有太大区别,都是基于地理位置相近的两个物体之间的信号传输。
  但NFC与RFID还是有区别的,NFC技术增加了点对点通信功能,可以快速建立蓝牙设备之间的P2P(点对点)无线通信,NFC设备彼此寻找对方并建立通信连接。P2P通信的双方设备是对等的,而RFID通信的双方设备是主从关系。  
  其余还有一些技术细节方面:
  NFC相较于RFID技术,具有距离近、带宽高、能耗低等一些特点。详细内容:
  1.NFC只是限于13.56MHz的频段!而RFID的频段有低频(125KHz到135KHz),高频(13.56MHz)和超高频(860MHz到960MHz之间。
  2.工作有效距离:NFC(小于10cm,所以具有很高的安全性),RFID距离从几米到几十米都有!
  3.因为同样工作于13.56MHz,NFC与现有非接触智能卡技术兼容,所以很多的厂商和相关团体都支持NFC,而RFID标准较多,统一较为复杂(估计是没可能统一的了),只能在特殊行业有特殊需求下,采用相应的技术标准!
  4.应用:RFID更多的被应用在生产、物流、跟踪、资产管理上,而NFC则在门禁、公交、手机支付等领域内发挥着巨大的作用。
Java多线程设计模式(4)线程池模式 thread http://computerdragon.blog.51cto.com/6235984/1205324
http://computerdragon.blog.51cto.com/6235984/1205324
java多线程总结五:线程池的原理及实现 thread http://blog.csdn.net/touch_2011/article/details/6914468
http://blog.csdn.net/touch_2011/article/details/6914468
lucene + hadoop 分布式搜索运行框架 bigdata http://code.google.com/p/nutla/
1、概述

不管程序性能有多高,机器处理能力有多强,都会有其极限。能够快速方便的横向与纵向扩展是Nut设计最重要的原则,以此原则形成以分布式并行计算为核心的架构设计。以分布式并行计算为核心的架构设计是Nut区别于Solr、Katta的地方。
Nut是一个Lucene+Hadoop分布式并行计算搜索框架,能对千G以上索引提供7*24小时搜索服务。在服务器资源足够的情况下能达到每秒处理100万次的搜索请求。 Nut开发环境:jdk1.6.0.23+lucene3.0.3+eclipse3.6.1+hadoop0.20.2+zookeeper3.3.2+hbase0.20.6+memcached+mongodb+linux

2、特新

a、热插拔 b、可扩展 c、高负载 d、易使用,与现有项目无缝集成
e、支持排序 f、7*24服务 g、失败转移
3、搜索流程 Nut由Index、Search、Client、Cache和DB五部分构成。(Cache实现了对memcached的支持,DB实现了对hbase,mongodb的支持) Client处理用户请求和对搜索结果排序。Search对请求进行搜索,Search上只放索引,数据存储在DB中,Nut将索引和存储分离。Cache缓存的是搜索条件和结果文档id。DB存储着数据,Client根据搜索排序结果,取出当前页中的文档id从DB上读取数据。

用户发起搜索请求给由Nut Client构成的集群,由某个Nut Client根据搜索条件查询Cache服务器是否有该缓存,如果有缓存根据缓存的文档id直接从DB读取数据,如果没有缓存将随机选择一组搜索服务器组(Search Group i),将查询条件同时发给该组搜索服务器组里的n台搜索服务器,搜索服务器将搜索结果返回给Nut Client由其排序,取出当前页文档id,将搜索条件和当前文档id缓存,同时从DB读取数据。

4、索引流程 Hadoop Mapper/Reducer 建立索引。再将索引从HDFS分发到各个索引服务器。 对索引的更新分为两种:删除和添加(更新分解为删除和添加)。 a、删除 在HDFS上删除索引,将生成的.del文件分发到所有的索引服务器上去或者对HDFS索引目录删除索引再分发到对应的索引服务器上去。 b、添加 新添加的数据用另一台服务器来生成。 删除和添加步骤可按不同定时策略来实现。

5、Nut分布式并行计算特点 Nut分布式并行计算虽然也是基于M/R模型,但是与Hadoop M/R模型是不同的。在Hadoop M/R模型中 Mapper和Reducer是一个完整的流程,Reducer依赖于Mapper。数据源通过Mapper分发本身就会消耗大量的I/O,并且是消耗I/O最大的部分。所以Hadoop M/R 并发是有限的。 Nut M/R模型是将Mapper和Reducer分离,各自独立存在。在Nut中 索引以及索引管理 构成M,搜索以及搜索服务器组 构成 R。 以一个分类统计来说明Nut分布式并行计算的流程。假设有10个分类,对任意关键词搜索要求统计出该关键词在这10个分类中的总数。同时假设有10组搜索服务器。索引以及索引管理进行索引数据的Mapper,这块是后台独自运行管理的。Nut Client将这10个分类统计分发到10组搜索服务器上,每组搜索服务器对其中一个分类进行Reducer,并且每组搜索服务器可进行多级Reducer。最后将最终结果返回给Nut Client。

6、设计图

7、Zookeeper服务器状态管理策略

在架构设计上通过使用多组搜索服务器可以支持每秒处理100万个搜索请求。 每组搜索服务器能处理的搜索请求数在1万—1万5千之间。如果使用100组搜索服务器,理论上每秒可处理100万个搜索请求。

假如每组搜索服务器有100份索引放在100台正在运行中搜索服务器(run)上,那么将索引按照如下的方式放在备用中搜索服务器(bak)上:index 1,index 2,index 3,index 4,index 5,index 6,index 7,index 8,index 9,index 10放在B 1 上,index 6,index 7,index 8,index 9,index 10,index 11,index 12,index 13,index 14,index 15放在B 2上。。。。。。index 96,index 97,index 98,index 99,index 100,index 5,index 4,index 3,index 2,index 1放在最后一台备用搜索服务器上。那么每份索引会存在3台机器中(1份正在运行中,2份备份中)。 尽管这样设计每份索引会存在3台机器中,仍然不是绝对安全的。假如运行中的index 1,index 2,index 3同时宕机的话,那么就会有一份索引搜索服务无法正确启用。这样设计,作者认为是在安全性和机器资源两者之间一个比较适合的方案。

备用中的搜索服务器会定时检查运行中搜索服务器的状态。一旦发现与自己索引对应的服务器宕机就会向lock申请分布式锁,得到分布式锁的服务器就将自己加入到运行中搜索服务器组,同时从备用搜索服务器组中删除自己,并停止运行中搜索服务器检查服务。

为能够更快速的得到搜索结果,设计上将搜索服务器分优先等级。通常是将最新的数据放在一台或几台内存搜索服务器上。通常情况下前几页数据能在这几台搜索服务器里搜索到。如果在这几台搜索服务器上没有数据时再向其他旧数据搜索服务器上搜索。 优先搜索等级的逻辑是这样的:9最大为搜索全部服务器并且9不能作为level标识。当搜索等级level为1,搜索优先级为1的服务器,当level为2时搜索优先级为1和2的服务器,依此类推。
风情QQ qq http://fengqing.blogcn.com/articles/1.html
http://fengqing.blogcn.com/articles/1.html
UML&开发过程 uml http://blog.163.com/haizai219@126/blog/static/44412555201021124214648/

2010-03-11 14:42:14|  分类: UML |  标签:uml  开发过程   |字号 订阅
UML学习
学习UML的三个阶段,就是要解决三个问题:
1.       如何使用UML画图?(How)
2.       为什么要使用UML?(Why)
3.       什么时候使用UML?(When)
三个问题对应三个阶段(Stage):
Stage1:学习UML的基本语法,包括各种UML的大图适用情形。目标是能够看懂UML图,理解模型所表达的意思,能够使用UML工具表达简单的建模思想。
Stage2:深入学习UML语法,理解UML建模的功能特征,体会UML建模给软件设计及开发过程带来的好处。目标是能够在实际应用中灵活地扩展和修改它。
Stage3:深入学习UML和开发过程,将UML与开发过程结合起来。目的是找到UML发挥威力的地方施展开来。
 
开发过程方法学(Methodology)就是开发过程中经历的步骤的结构和性质。
开发过程方法学:
1.       瀑布模型(waterfall)
四个阶段:分析Analysis,设计Design,编码Coding,部署Deployment
缺点:信息共享少,过程不能回溯到早期阶段,不利于问题的逐步理解
2.       现代开发过程
1)  强调阶段的无缝集成
2)  项目由多人协作完成,按角色分工
系统分析员:与客户交流,理解业务领域问题;
系统架构师:设计问题的解决方案;
程序设计人员:将解决方案编制成代码,交付可执行的目标系统;
系统工程师:将目标系统部署到硬件上运行;
项目经理:监控开发过程进度以及形成职责跟踪的工作产品,向客户报告项目的最新进展;
3)  GRAPPLE(Guidelines for Rapid APPLication Engineering)快速应用工程指导原则,一个自适应的、灵活的开发思想。
五个段(Segment)组成一个过程RADDD(或RAD3):
1.       需求收集(Requirements Gathering)
2.       分析(Analysis)
3.       设计(Design)
4.       开发(Development)
5.       部署(Deployment)
每个段有一些动作(Action)组成,每个动作产生一个工作产品,每个动都是其特定执行者(Player)的职责。
Java 程序员在写 SQL 程序时候常犯的 10 个错误 sql http://www.oschina.net/translate/10-common-mistakes-java-developers-make-when-writing-sql
Java程序员编程时需要混合面向对象思维和一般命令式编程的方法,能否完美的将两者结合起来完全得依靠编程人员的水准:
技能(任何人都能容易学会命令式编程)
模式(有些人用“模式-模式”,举个例子,模式可以应用到任何地方,而且都可以归为某一类模式)
心境(首先,要写个好的面向对象程序是比命令式程序难的多,你得花费一些功夫)
但当Java程序员写SQL语句时,一切都不一样了。SQL是说明性语言而非面向对象或是命令式编程语言。在SQL中要写个查询语句是很简单的。但在Java里类似的语句却不容易,因为程序员不仅要反复考虑编程范式,而且也要考虑算法的问题。
下面是Java程序员在写SQL时常犯的错误(没有特定的顺序):


1.忘掉NULL

Java程序员写SQL时对NULL的误解可能是最大的错误。也许是因为(并非唯一理由)NULL也称作UNKNOWN。如果被称作UNKNOWN,这还好理解些。另一个原因是,当你从数据库拿东西或是绑定变量时,JDBC将SQL NULL 和Java中的null对应了起来。这样导致了NULL = NULL(SQL)和null=null(Java)的误解。
对于NULL最大的误解是当NULL被用作行值表达式完整性约束条件时。
另一个误解出现在对于NULL 在 NOT IN anti-joins的应用中。
解决方法:
好好的训练你自己。当你写SQL时要不停得想到NULL的用法:
这个NULL完整性约束条件是正确的?
NULL是否影响到结果?


2.在Java内存中处理数据

很少有Java开发者能将SQL理解的很好.偶尔使用的JOIN,还有古怪的UNION,好吧.但是对于窗口函数呢?还有对集合进行分组呢?许多的Java开发者将SQL数据加载到内存中,将这些数据转换成某些相近的集合类型,然后再那些集合上面使用边界循环控制结构(至少在Java8的集合升级以前)执行令人生厌的数学运算.
但是一些SQL数据库支持先进的(而且是SQL标准支持的!)OLAP特性,这一特性表现更好而且写起来也更加方便.一个(并不怎么标准的)例子就是Oracle超棒的MODEL分句.只让数据库来做处理然后只把结果带到Java内存中吧.因为毕竟所有非常聪明的家伙已经对这些昂贵的产品进行了优化.因此实际上,通过将OLAP移到数据库,你将获得一下两项好处:
便利性.这比在Java中编写正确的SQL可能更加的容易.
性能表现.数据库应该比你的算法处理起来更加快.而且更加重要的是,你不必再去传递数百万条记录了.
完善的方法:
每次你使用Java实现一个以数据为中心的算法时,问问自己:有没有一种方法可以让数据库代替为我做这种麻烦事.


3. 使用UNION代替UNION ALL

太可耻了,和UNION相比UNION ALL还需要额外的关键字。如果SQL标准已经规定了支持,那么可能会更好点。
UNION(允许重复)
UNION DISTINCT (去除了重复)
移除重复行不仅很少需要(有时甚至是错的),而且对于带很多行的大数据集合会相当慢,因为两个子select需要排序,而且每个元组也需要和它的子序列元组比较。
注意即使SQL标准规定了INTERSECT ALL和EXCEPT ALL,很少数据库会实现这些没用的集合操作符。
处理方法:
每次你写UNION语句时,考虑实际上是否需要UNION ALL语句。


4.通过JDBC分页技术给大量的结果进行分页操作

大部分的数据库都会支持一些分页命令实现分页效果,譬如LIMIT..OFFSET,TOP..START AT,OFFSET..FETCH语句等。即使没有支持这些语句的数据库,仍有可能对ROWNUM(甲骨文)或者是ROW NUMBER() OVER()过滤(DB2,SQL Server2008等),这些比在内存中实现分页更快速。在处理大量数据中,效果尤其明显。
纠正:
仅仅使用这些语句,那么一个工具(例如JOOQ)就可以模拟这些语句的操作。


5.在java内存中加入数据

从SQL的初期开始,当在SQL中使用JOIN语句时,一些开发者仍旧有不安的感觉。这是源自对加入JOIN后会变慢的固有恐惧。假如基于成本的优化选择去实现嵌套循环,在创建一张连接表源前,可能加载所有的表在数据库内存中,这可能是真的。但是这事发生的概率太低了。通过合适的预测,约束和索引,合并连接和哈希连接的操作都是相当的快。这完全是是关于正确元数据(在这里我不能够引用Tom Kyte的太多)。而且,可能仍然有不少的Java开发人员加载两张表通过分开查询到一个映射中,并且在某种程度上把他们加到了内存当中。
纠正:
假如你在各个步骤中有从各种表的查询操作,好好想想是否可以表达你的查询操作在单条语句中。


6.在一个临时的笛卡尔积集合中使用 DISTINCT 或 UNION 消除重复项

通过复杂的连接,人们可能会对SQL语句中扮演关键角色的所有关系失去概念。特别的,如果这涉及到多列外键关系的话,很有可能会忘记在JOIN .. ON子句中增加相关的判断。这会导致重复的记录,但或许只是在特殊的情况下。有些开发者因此可能选择DISTINCT来消除这些重复记录。从三个方面来说这是错误的:
它(也许)解决了表面症状但并没有解决问题。它也有可能无法解决极端情况下的症状。
对具有很多列的庞大的结果集合来说它很慢。DISTINCT要执行ORDER BY操作来消除重复。
对庞大的笛卡尔积集合来说它很慢,还是需要加载很多的数据到内存中。
解决方法:
根据经验,如果你获得了不需要的重复记录,还是检查你的JOIN判断吧。可能在某个地方有一个很难觉察的笛卡尔积集合。


7. 不使用MERGE语句

这并不是一个过失,但是可能是缺少知识或者对于强悍的MERGE语句信心不足。一些数据库理解其它形式的更新插入(UPSERT)语句, 如 MYSQL的重复主键更新语句,但是MERGE在数据库中确是很强大,很重要,以至于大肆扩展SQL标准,例如SQL SERVER。
解决之道:
如果你使用像联合INSERT和UPDATE或者联合SELECT .. FOR UPDATE然后在INSERT或UPDATE等更新插入时,请三思。你完全可以使用一个更简单的MERGE语句来远离冒险竞争条件。


8. 使用聚合函数代替窗口函数(window functions)

在介绍窗口函数之前,在SQL中聚合数据意味着使用GROUP BY语句与聚合函数相映射。在很多情形下都工作得很好,如聚合数据需要浓缩常规数据,那么就在join子查询中使用group查询。
但是在SQL:2003中定义了窗口函数,这个在很多主流数据库都实现了它。窗口函数能够在结果集上聚合数据,但是却没有分组。事实上,每个窗口函数都有自己的、独立的PARTITION BY语句,这个工具对于显示报告太TM好了。
使用窗口函数:
使SQL更易读(但在子查询中没有GROUP BY语句专业)
提升性能,像关系数据库管理系统能够更容易优化窗口函数
解决方法:
当你在子查询中使用GROUP BY语句时,请再三考虑是否可以使用窗口函数完成。


9. 使用内存间接排序

SQL的ORDER BY语句支持很多类型的表达式,包括CASE语句,对于间接排序十分有用。你可能重来不会在Java内存中排序数据,因为你会想:
SQL排序很慢
SQL排序办不到
处理方法:
如果你在内存中排序任何SQL数据,请再三考虑,是否不能在数据库中排序。这对于数据库分页数据十分有用。


10. 一条一条的插入大量纪录

JDBC ”懂“批处理(batch),你应该不会忘了它。不要使用INSERT语句来一条一条的出入成千上万的记录,(因为)每次都会创建一个新的PreparedStatement对象。如果你的所有记录都插入到同一个表时,那么就创建一个带有一条SQL语句以及附带很多值集合的插入批处理语句。你可能需要在达到一定量的插入记录后才提交来保证UNDO日志瘦小,这依赖于你的数据库和数据库设置。
处理方法:
总是使用批处理插入大量数据。
10个很有用的 jQuery Google 地图插件 jquery

Google 地图提供很多 API ,用来在你的网站中使用地图功能,但很多 API 使用比较复杂而且难以理解,使用 jQuery 的地图插件可以帮你简化集成地图的开发。

1) Jquery GPS


2) Jquery gMap


3) jMapping

4) goMap jQuery Google Maps Plugin


5) JQuery bMap Plugin


6) JQuery Mapbox


7) IM Google Maps


8 ) JQuery and Google Map


9) My JQuery map


10) gMap3


11) Google Map using JQuery


Via:http://zoomzum.com/the-10-useful-jquery-google-map-plugins/
优化SQL查询:如何写出高性能SQL语句 database
1、 首先要搞明白什么叫执行计划?

执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式。

可见,执行计划并不是固定的,它是“个性化的”。产生一个正确的“执行计划”有两点很重要:

(1)    SQL语句是否清晰地告诉查询优化器它想干什么?

(2)    查询优化器得到的数据库统计信息是否是最新的、正确的?


 
2、 统一SQL语句的写法

对于以下两句SQL语句,程序员认为是相同的,数据库查询优化器认为是不同的。 


select*from dual 

select*From dual

 

其实就是大小写不同,查询分析器就认为是两句不同的SQL语句,必须进行两次解析。生成2个执行计划。所以作为程序员,应该保证相同的查询语句在任何地方都一致,多一个空格都不行!

3、 不要把SQL语句写得太复杂

我经常看到,从数据库中捕捉到的一条SQL语句打印出来有2张A4纸这么长。一般来说这么复杂的语句通常都是有问题的。我拿着这2页长的SQL语句去请教原作者,结果他说时间太长,他一时也看不懂了。可想而知,连原作者都有可能看糊涂的SQL语句,数据库也一样会看糊涂。

一般,将一个Select语句的结果作为子集,然后从该子集中再进行查询,这种一层嵌套语句还是比较常见的,但是根据经验,超过3层嵌套,查询优化器就很容易给出错误的执行计划。因为它被绕晕了。像这种类似人工智能的东西,终究比人的分辨力要差些,如果人都看晕了,我可以保证数据库也会晕的。

另外,执行计划是可以被重用的,越简单的SQL语句被重用的可能性越高。而复杂的SQL语句只要有一个字符发生变化就必须重新解析,然后再把这一大堆垃圾塞在内存里。可想而知,数据库的效率会何等低下。

4、 使用“临时表”暂存中间结果

简化SQL语句的重要方法就是采用临时表暂存中间结果,但是,临时表的好处远远不止这些,将临时结果暂存在临时表,后面的查询就在tempdb中了,这可以避免程序中多次扫描主表,也大大减少了程序执行中“共享锁”阻塞“更新锁”,减少了阻塞,提高了并发性能。

5、 OLTP系统SQL语句必须采用绑定变量 


select*from orderheader where changetime >'2010-10-20 00:00:01' 
select*from orderheader where changetime >'2010-09-22 00:00:01'


 

以上两句语句,查询优化器认为是不同的SQL语句,需要解析两次。如果采用绑定变量


select*from orderheader where changetime >@chgtime

@chgtime变量可以传入任何值,这样大量的类似查询可以重用该执行计划了,这可以大大降低数据库解析SQL语句的负担。一次解析,多次重用,是提高数据库效率的原则。

6、 绑定变量窥测

事物都存在两面性,绑定变量对大多数OLTP处理是适用的,但是也有例外。比如在where条件中的字段是“倾斜字段”的时候。

“倾斜字段”指该列中的绝大多数的值都是相同的,比如一张人口调查表,其中“民族”这列,90%以上都是汉族。那么如果一个SQL语句要查询30岁的汉族人口有多少,那“民族”这列必然要被放在where条件中。这个时候如果采用绑定变量@nation会存在很大问题。

试想如果@nation传入的第一个值是“汉族”,那整个执行计划必然会选择表扫描。然后,第二个值传入的是“布依族”,按理说“布依族”占的比例可能只有万分之一,应该采用索引查找。但是,由于重用了第一次解析的“汉族”的那个执行计划,那么第二次也将采用表扫描方式。这个问题就是著名的“绑定变量窥测”,建议对于“倾斜字段”不要采用绑定变量。

7、 只在必要的情况下才使用begin tran

SQL Server中一句SQL语句默认就是一个事务,在该语句执行完成后也是默认commit的。其实,这就是begin tran的一个最小化的形式,好比在每句语句开头隐含了一个begin tran,结束时隐含了一个commit。

有些情况下,我们需要显式声明begin tran,比如做“插、删、改”操作需要同时修改几个表,要求要么几个表都修改成功,要么都不成功。begin tran 可以起到这样的作用,它可以把若干SQL语句套在一起执行,最后再一起commit。好处是保证了数据的一致性,但任何事情都不是完美无缺的。Begin tran付出的代价是在提交之前,所有SQL语句锁住的资源都不能释放,直到commit掉。

可见,如果Begin tran套住的SQL语句太多,那数据库的性能就糟糕了。在该大事务提交之前,必然会阻塞别的语句,造成block很多。

Begin tran使用的原则是,在保证数据一致性的前提下,begin tran 套住的SQL语句越少越好!有些情况下可以采用触发器同步数据,不一定要用begin tran。

8、 一些SQL查询语句应加上nolock

在SQL语句中加nolock是提高SQL Server并发性能的重要手段,在oracle中并不需要这样做,因为oracle的结构更为合理,有undo表空间保存“数据前影”,该数据如果在修改中还未commit,那么你读到的是它修改之前的副本,该副本放在undo表空间中。这样,oracle的读、写可以做到互不影响,这也是oracle 广受称赞的地方。SQL Server 的读、写是会相互阻塞的,为了提高并发性能,对于一些查询,可以加上nolock,这样读的时候可以允许写,但缺点是可能读到未提交的脏数据。使用 nolock有3条原则。

(1)    查询的结果用于“插、删、改”的不能加nolock !

(2)    查询的表属于频繁发生页分裂的,慎用nolock !

(3)    使用临时表一样可以保存“数据前影”,起到类似oracle的undo表空间的功能,

能采用临时表提高并发性能的,不要用nolock 。

9、 聚集索引没有建在表的顺序字段上,该表容易发生页分裂

比如订单表,有订单编号orderid,也有客户编号contactid,那么聚集索引应该加在哪个字段上呢?对于该表,订单编号是顺序添加的,如果在orderid上加聚集索引,新增的行都是添加在末尾,这样不容易经常产生页分裂。然而,由于大多数查询都是根据客户编号来查的,因此,将聚集索引加在contactid上才有意义。而contactid对于订单表而言,并非顺序字段。

比如“张三”的“contactid”是001,那么“张三”的订单信息必须都放在这张表的第一个数据页上,如果今天“张三”新下了一个订单,那该订单信息不能放在表的最后一页,而是第一页!如果第一页放满了呢?很抱歉,该表所有数据都要往后移动为这条记录腾地方。

SQL Server的索引和Oracle的索引是不同的,SQL Server的聚集索引实际上是对表按照聚集索引字段的顺序进行了排序,相当于oracle的索引组织表。SQL Server的聚集索引就是表本身的一种组织形式,所以它的效率是非常高的。也正因为此,插入一条记录,它的位置不是随便放的,而是要按照顺序放在该放的数据页,如果那个数据页没有空间了,就引起了页分裂。所以很显然,聚集索引没有建在表的顺序字段上,该表容易发生页分裂。

曾经碰到过一个情况,一位哥们的某张表重建索引后,插入的效率大幅下降了。估计情况大概是这样的。该表的聚集索引可能没有建在表的顺序字段上,该表经常被归档,所以该表的数据是以一种稀疏状态存在的。比如张三下过20张订单,而最近3个月的订单只有5张,归档策略是保留3个月数据,那么张三过去的 15张订单已经被归档,留下15个空位,可以在insert发生时重新被利用。在这种情况下由于有空位可以利用,就不会发生页分裂。但是查询性能会比较低,因为查询时必须扫描那些没有数据的空位。

重建聚集索引后情况改变了,因为重建聚集索引就是把表中的数据重新排列一遍,原来的空位没有了,而页的填充率又很高,插入数据经常要发生页分裂,所以性能大幅下降。

对于聚集索引没有建在顺序字段上的表,是否要给与比较低的页填充率?是否要避免重建聚集索引?是一个值得考虑的问题!

10、加nolock后查询经常发生页分裂的表,容易产生跳读或重复读

加nolock后可以在“插、删、改”的同时进行查询,但是由于同时发生“插、删、改”,在某些情况下,一旦该数据页满了,那么页分裂不可避免,而此时nolock的查询正在发生,比如在第100页已经读过的记录,可能会因为页分裂而分到第101页,这有可能使得nolock查询在读101页时重复读到该条数据,产生“重复读”。同理,如果在100页上的数据还没被读到就分到99页去了,那nolock查询有可能会漏过该记录,产生“跳读”。

上面提到的哥们,在加了nolock后一些操作出现报错,估计有可能因为nolock查询产生了重复读,2条相同的记录去插入别的表,当然会发生主键冲突。

11、使用like进行模糊查询时应注意

有的时候会需要进行一些模糊查询比如


select*from contact where username like ‘%yue%’

 

关键词%yue%,由于yue前面用到了“%”,因此该查询必然走全表扫描,除非必要,否则不要在关键词前加%,

12、数据类型的隐式转换对查询效率的影响

sql server2000的数据库,我们的程序在提交sql语句的时候,没有使用强类型提交这个字段的值,由sql server 2000自动转换数据类型,会导致传入的参数与主键字段类型不一致,这个时候sql server 2000可能就会使用全表扫描。Sql2005上没有发现这种问题,但是还是应该注意一下。

13、SQL Server 表连接的三种方式

(1) Merge Join

(2) Nested Loop Join

(3) Hash Join

SQL Server 2000只有一种join方式——Nested Loop Join,如果A结果集较小,那就默认作为外表,A中每条记录都要去B中扫描一遍,实际扫过的行数相当于A结果集行数x B结果集行数。所以如果两个结果集都很大,那Join的结果很糟糕。

SQL Server 2005新增了Merge Join,如果A表和B表的连接字段正好是聚集索引所在字段,那么表的顺序已经排好,只要两边拼上去就行了,这种join的开销相当于A表的结果集行数加上B表的结果集行数,一个是加,一个是乘,可见merge join 的效果要比Nested Loop Join好多了。

如果连接的字段上没有索引,那SQL2000的效率是相当低的,而SQL2005提供了Hash join,相当于临时给A,B表的结果集加上索引,因此SQL2005的效率比SQL2000有很大提高,我认为,这是一个重要的原因。

总结一下,在表连接时要注意以下几点:

(1)    连接字段尽量选择聚集索引所在的字段

(2)    仔细考虑where条件,尽量减小A、B表的结果集

(3)    如果很多join的连接字段都缺少索引,而你还在用SQL Server 2000,赶紧升级吧。
MySQL数据库性能优化之表结构优化 database
由于MySQL数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 page(block)的方式,也就是说,如果我们每条记录所占用的空间量减小,就会使每个page中可存放的数据行数增大,那么每次 IO 可访问的行数也就增多了。反过来说,处理相同行数的数据,需要访问的 page 就会减少,也就是 IO 操作次数降低,直接提升性能。此外,由于我们的内存是有限的,增加每个page中存放的数据行数,就等于增加每个内存块的缓存数据量,同时还会提升内存换中数据命中的几率,也就是缓存命中率。
  数据类型选择
  数据库操作中最为耗时的操作就是 IO 处理,大部分数据库操作 90% 以上的时间都花在了 IO 读写上面。所以尽可能减少 IO 读写量,可以在很大程度上提高数据库操作的性能。
  我们无法改变数据库中需要存储的数据,但是我们可以在这些数据的存储方式方面花一些心思。下面的这些关于字段类型的优化建议主要适用于记录条数较多,数据量较大的场景,因为精细化的数据类型设置可能带来维护成本的提高,过度优化也可能会带来其他的问题:
  1.数字类型:非万不得已不要使用DOUBLE,不仅仅只是存储长度的问题,同时还会存在精确性的问题。同样,固定精度的小数,也不建议使用DECIMAL,建议乘以固定倍数转换成整数存储,可以大大节省存储空间,且不会带来任何附加维护成本。对于整数的存储,在数据量较大的情况下,建议区分开 TINYINT / INT / BIGINT 的选择,因为三者所占用的存储空间也有很大的差别,能确定不会使用负数的字段,建议添加unsigned定义。当然,如果数据量较小的数据库,也可以不用严格区分三个整数类型。
  2.字符类型:非万不得已不要使用 TEXT 数据类型,其处理方式决定了他的性能要低于char或者是varchar类型的处理。定长字段,建议使用 CHAR 类型,不定长字段尽量使用 VARCHAR,且仅仅设定适当的最大长度,而不是非常随意的给一个很大的最大长度限定,因为不同的长度范围,MySQL也会有不一样的存储处理。
  3.时间类型:尽量使用TIMESTAMP类型,因为其存储空间只需要 DATETIME 类型的一半。对于只需要精确到某一天的数据类型,建议使用DATE类型,因为他的存储空间只需要3个字节,比TIMESTAMP还少。不建议通过INT类型类存储一个unix timestamp 的值,因为这太不直观,会给维护带来不必要的麻烦,同时还不会带来任何好处。
  4.ENUM & SET:对于状态字段,可以尝试使用 ENUM 来存放,因为可以极大的降低存储空间,而且即使需要增加新的类型,只要增加于末尾,修改结构也不需要重建表数据。如果是存放可预先定义的属性数据呢?可以尝试使用SET类型,即使存在多种属性,同样可以游刃有余,同时还可以节省不小的存储空间。
  5.LOB类型:强烈反对在数据库中存放 LOB 类型数据,虽然数据库提供了这样的功能,但这不是他所擅长的,我们更应该让合适的工具做他擅长的事情,才能将其发挥到极致。在数据库中存储 LOB 数据就像让一个多年前在学校学过一点Java的营销专业人员来写 Java 代码一样。
  字符编码
  字符集直接决定了数据在MySQL中的存储编码方式,由于同样的内容使用不同字符集表示所占用的空间大小会有较大的差异,所以通过使用合适的字符集,可以帮助我们尽可能减少数据量,进而减少IO操作次数。
  1.纯拉丁字符能表示的内容,没必要选择 latin1 之外的其他字符编码,因为这会节省大量的存储空间。
  2.如果我们可以确定不需要存放多种语言,就没必要非得使用UTF8或者其他UNICODE字符类型,这回造成大量的存储空间浪费。
  3.MySQL的数据类型可以精确到字段,所以当我们需要大型数据库中存放多字节数据的时候,可以通过对不同表不同字段使用不同的数据类型来较大程度减小数据存储量,进而降低 IO 操作次数并提高缓存命中率。
  适当拆分
  有些时候,我们可能会希望将一个完整的对象对应于一张数据库表,这对于应用程序开发来说是很有好的,但是有些时候可能会在性能上带来较大的问题。
  当我们的表中存在类似于 TEXT 或者是很大的 VARCHAR类型的大字段的时候,如果我们大部分访问这张表的时候都不需要这个字段,我们就该义无反顾的将其拆分到另外的独立表中,以减少常用数据所占用的存储空间。这样做的一个明显好处就是每个数据块中可以存储的数据条数可以大大增加,既减少物理 IO 次数,也能大大提高内存中的缓存命中率。
  上面几点的优化都是为了减少每条记录的存储空间大小,让每个数据库中能够存储更多的记录条数,以达到减少 IO 操作次数,提高缓存命中率。下面这个优化建议可能很多开发人员都会觉得不太理解,因为这是典型的反范式设计,而且也和上面的几点优化建议的目标相违背。
  适度冗余
  为什么我们要冗余?这不是增加了每条数据的大小,减少了每个数据块可存放记录条数吗?
  确实,这样做是会增大每条记录的大小,降低每条记录中可存放数据的条数,但是在有些场景下我们仍然还是不得不这样做:
  1.被频繁引用且只能通过 Join 2张(或者更多)大表的方式才能得到的独立小字段。
  2.这样的场景由于每次Join仅仅只是为了取得某个小字段的值,Join到的记录又大,会造成大量不必要的 IO,完全可以通过空间换取时间的方式来优化。不过,冗余的同时需要确保数据的一致性不会遭到破坏,确保更新的同时冗余字段也被更新。
  尽量使用 NOT NULL
  NULL 类型比较特殊,SQL 难优化。虽然 MySQL NULL类型和 Oracle 的NULL 有差异,会进入索引中,但如果是一个组合索引,那么这个NULL 类型的字段会极大影响整个索引的效率。此外,NULL 在索引中的处理也是特殊的,也会占用额外的存放空间。
  很多人觉得 NULL 会节省一些空间,所以尽量让NULL来达到节省IO的目的,但是大部分时候这会适得其反,虽然空间上可能确实有一定节省,倒是带来了很多其他的优化问题,不但没有将IO量省下来,反而加大了SQL的IO量。所以尽量确保 DEFAULT 值不是 NULL,也是一个很好的表结构设计优化习惯。
java代码优化 java
可供程序利用的资源(内存、CPU时间、网络带宽等)是有限的,优化的目的就是让程序用尽可能少的资源完成预定的任务。优化通常包含两方面的内容:减小代码的体积,提高代码的运行效率。本文讨论的主要是如何提高代码的效率。

在Java程序中,性能问题的大部分原因并不在于Java语言,而是在于程序本身。养成好的代码编写习惯非常重要,比如正确地、巧妙地运用java.lang.String类和java.util.Vector类,它能够显著地提高程序的性能。下面我们就来具体地分析一下这方面的问题。

1.尽量指定类的final修饰符 带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50% 。

2.尽量重用对象。特别是String 对象的使用中,出现字符串连接情况时应用StringBuffer 代替。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。

3.尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。

4.不要重复初始化变量  默认情况下,调用类的构造函数时, Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。

5.在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。

6. Java 编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,即使关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎,会导致严重的后果。

7.由于JVM的有其自身的GC机制,不需要程序开发者的过多考虑,从一定程度上减轻了开发者负担,但同时也遗漏了隐患,过分的创建对象会消耗系统的大量内存,严重时会导致内存泄露,因此,保证过期对象的及时回收具有重要意义。JVM回收垃圾的条件是:对象不在被引用;然而,JVM的GC并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成null。

8.在使用同步机制时,应尽量使用方法同步代替代码块同步。

9.尽量减少对变量的重复计算
Java代码  
例如:for(int i = 0;i < list.size; i ++) {  
            …  
}  
应替换为:  
for(int i = 0,int len = list.size();i < len; i ++) {  
            …  
}  
 

10.尽量采用lazy loading 的策略,即在需要的时候才开始创建。
Java代码  
    例如:    String str = “aaa”;  
            if(i == 1) {  
                list.add(str);  
}  
应替换为:  
            if(i == 1) {  
String str = “aaa”;  
                list.add(str);  
}  
 
11.慎用异常 
异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。 异常只能用于错误处理,不应该用来控制程序流程。

12.不要在循环中使用:
Java代码  
Try {  
} catch() {  
}  
 
应把其放置在最外层。

13.StringBuffer 的使用:
        StringBuffer表示了可变的、可写的字符串。
有三个构造方法 :
Java代码  
StringBuffer ();            //默认分配16个字符的空间  
StringBuffer (int size);  //分配size个字符的空间  
StringBuffer (String str);  //分配16个字符+str.length()个字符空间   
 
   你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。这里提到的构造函数是StringBuffer(int length),length参数表示当前的StringBuffer能保持的字符数量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer对象创建之后设置它的容量。首先我们看看StringBuffer的缺省行为,然 后再找出一条更好的提升性能的途径。
     StringBuffer在内部维护一个字符数组,当你使用缺省的构造函数来创建StringBuffer对象的时候,因为没有设置初始化字符长度,StringBuffer的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当StringBuffer达到最大容量 的时候,它会将自身容量增加到当前的2倍再加2,也就是(2*旧值+2)。如果你使用缺省值,初始化之后接着往里面追 加字符,在你追加到第16个字符的时候它会将容量增加到34(2*16+2),当追加到34个字符的时候就会将容量增加到 70(2*34+2)。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和 新字符都拷贝一遍――这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来 立竿见影的性能增益。
     StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。

14.合理的使用Java类 java.util.Vector。
简单地说,一个Vector就是一个java.lang.Object实例的数组。Vector与数组相似,它的元素可以通过整数形式的索引访问。但是,Vector类型的对象在创建之后,对象的大小能够根据元素的增加或者删除而扩展、缩小。请考虑下面这个向Vector加入元素的例子:
Java代码  
Object obj = new Object();  
Vector v = new Vector(100000);  
for(int I=0;  
I<100000; I++) { v.add(0,obj); }  
 

  除非有绝对充足的理由要求每次都把新元素插入到Vector的前面,否则上面的代码对性能不利。在默认构造函数中,Vector的初始存储能力是10个元素,如果新元素加入时存储能力不足,则以后存储能力每次加倍。Vector类就象StringBuffer类一样,每次扩展存储能力时,所有现有的元素都要复制到新的存储空间之中。下面的代码片段要比前面的例子快几个数量级:
Java代码  
Object obj = new Object();  
Vector v = new Vector(100000);  
for(int I=0; I<100000; I++) { v.add(obj); }  
 

  同样的规则也适用于Vector类的remove()方法。由于Vector中各个元素之间不能含有“空隙”,删除除最后一个元素之外的任意其他元素都导致被删除元素之后的元素向前移动。也就是说,从Vector删除最后一个元素要比删除第一个元素“开销”低好几倍。

  假设要从前面的Vector删除所有元素,我们可以使用这种代码:
Java代码  
for(int I=0; I<100000; I++)  
{  
 v.remove(0);  
}  
 

  但是,与下面的代码相比,前面的代码要慢几个数量级:
Java代码  
for(int I=0; I<100000; I++)  
{  
 v.remove(v.size()-1);  
}  
 

  从Vector类型的对象v删除所有元素的最好方法是:
Java代码  
v.removeAllElements();  
 

  假设Vector类型的对象v包含字符串“Hello”。考虑下面的代码,它要从这个Vector中删除“Hello”字符串:
Java代码  
String s = "Hello";   
int i = v.indexOf(s);   
if(I != -1) v.remove(s);  
 
  这些代码看起来没什么错误,但它同样对性能不利。在这段代码中,indexOf()方法对v进行顺序搜索寻找字符串“Hello”,remove(s)方法也要进行同样的顺序搜索。改进之后的版本是:
Java代码  
String s = "Hello";  
int i = v.indexOf(s);  
if(I != -1) v.remove(i);  
 

  这个版本中我们直接在remove()方法中给出待删除元素的精确索引位置,从而避免了第二次搜索。一个更好的版本是:
Java代码  
String s = "Hello"; v.remove(s);  
 
  最后,我们再来看一个有关Vector类的代码片段:
Java代码  
for(int I=0; I++;I < v.length)  
 
  如果v包含100,000个元素,这个代码片段将调用v.size()方法100,000次。虽然size方法是一个简单的方法,但它仍旧需要一次方法调用的开销,至少JVM需要为它配置以及清除堆栈环境。在这里,for循环内部的代码不会以任何方式修改Vector类型对象v的大小,因此上面的代码最好改写成下面这种形式:
int size = v.size(); for(int I=0; I++;I<size)

  虽然这是一个简单的改动,但它仍旧赢得了性能。毕竟,每一个CPU周期都是宝贵的。

15.当复制大量数据时,使用System.arraycopy()命令。

16.代码重构:增强代码的可读性。
    例如:
Java代码  
public class ShopCart {  
        private List carts ;  
        …  
        public void add (Object item) {  
            if(carts == null) {  
                carts = new ArrayList();  
}  
crts.add(item);  
}  
public void remove(Object item) {  
    if(carts. contains(item)) {  
        carts.remove(item);  
}  
}  
public List getCarts() {  
    //返回只读列表  
    return Collections.unmodifiableList(carts);  
}  
  
//不推荐这种方式  
//this.getCarts().add(item);  
}  
 
17.不用new关键词创建类的实例 
用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。 
在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实
例非常简单。例如,
Java代码  
下面是Factory模式的一个典型实现:   
public static Credit getNewCredit() {  
    return new Credit();  
}   
改进后的代码使用clone()方法,如下所示:  
private static Credit BaseCredit = new Credit();  
public static Credit getNewCredit() {  
    return (Credit) BaseCredit.clone();  
}   
 
  上面的思路对于数组处理同样很有用。

18.乘法和除法 
Java代码  
考虑下面的代码:   
for (val = 0; val < 100000; val +=5) {  
alterX = val * 8; myResult = val * 2;  
}   
用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:   
for (val = 0; val < 100000; val += 5) {   
alterX = val << 3; myResult = val << 1;  
}  
 
修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。

19.在JSP页面中关闭无用的会话。
    一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <%@page session="false"%> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。
对于那些无需跟踪会话状态的页面,关闭自动创建的会话可以节省一些资源。使用如下page指令:<%@ page session="false"%>

20.JDBC与I/O 
如果应用程序需要访问一个规模很大的数据集,则应当考虑使用块提取方式。默认情况下,JDBC每次提取32行数据。举例来说,假设我们要遍历一个5000行的记录集,JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512,则调用数据库的次数将减少到10次。
 
21.Servlet与内存使用 
许多开发者随意地把大量信息保存到用户会话之中。一些时候,保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看,典型的症状是用户感到系统周期性地变慢,却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间,它的表现是内存占用不正常地大起大落。
解决这类内存问题主要有二种办法。第一种办法是,在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样,只要实现valueUnbound()方法,就可以显式地释放Bean使用的资源。 另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外,也可以用编程的方式调用会话的setMaxInactiveInterval()方法,该方法用来设定在作废会话之前,Servlet容器允许的客户请求的最大间隔时间,以秒计。

22、使用缓冲标记 
一些应用服务器加入了面向JSP的缓冲标记功能。例如,BEA的WebLogic Server从6.0版本开始支持这个功能,Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断,也能够缓冲整个页面。当JSP页面执行时,如果目标片断已经在缓冲之中,则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求,并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说,这个功能极其有用。对于这类应用,页面级缓冲能够保存页面执行的结果,供后继请求使用。

23、选择合适的引用机制
在典型的JSP应用系统中,页头、页脚部分往往被抽取出来,然后根据需要引入页头、页脚。当前,在JSP页面中引入外部资源的方法主要有两种:include指令,以及include动作。 
include指令:例如<%@ include file="copyright.html" %>。该指令在编译时引入指定的资源。在编译之前,带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定,比运行时才确定资源更高效。 
include动作:例如<jsp:include page="copyright.jsp" />。该动作引入指定页面执行后生成的结果。由于它在运行时完成,因此对输出结果的控制更加灵活。但时,只有当被引用的内容频繁地改变时,或者在对主页面的请求没有出现之前,被引用的页面无法确定时,使用include动作才合算。

24、及时清除不再需要的会话 
为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多会话时,如果内存容量不足,操作系统会把部分内存数据转移到磁盘,应用服务器也可能根据“最近最频繁使用”(Most Recently Used)算法把部分不活跃的会话转储到磁盘,甚至可能抛出“内存不足”异常。在大规模系统中,串行化会话的代价是很昂贵的。当会话不再需要时,应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。

25、不要将数组声明为:public static final 。

26、HashMap的遍历效率讨论
经常遇到对HashMap中的key和value值对的遍历操作,有如下两种方法:
Java代码  
Map<String, String[]> paraMap = new HashMap<String, String[]>();  
................//第一个循环  
Set<String> appFieldDefIds = paraMap.keySet();  
for (String appFieldDefId : appFieldDefIds) {  
String[] values = paraMap.get(appFieldDefId);  
......  
}  
  
//第二个循环  
for(Entry<String, String[]> entry : paraMap.entrySet()){  
String appFieldDefId = entry.getKey();  
String[] values = entry.getValue();  
.......  
}  
 

第一种实现明显的效率不如第二种实现。
分析如下 Set<String> appFieldDefIds = paraMap.keySet(); 是先从HashMap中取得keySet

代码如下:
Java代码  
public Set<K> keySet() {  
Set<K> ks = keySet;  
return (ks != null ? ks : (keySet = new KeySet()));  
}  
  
private class KeySet extends AbstractSet<K> {  
public Iterator<K> iterator() {  
return newKeyIterator();  
}  
public int size() {  
return size;  
}  
public boolean contains(Object o) {  
return containsKey(o);  
}  
public boolean remove(Object o) {  
return HashMap.this.removeEntryForKey(o) != null;  
}  
public void clear() {  
HashMap.this.clear();  
}  
}  
 
其实就是返回一个私有类KeySet, 它是从AbstractSet继承而来,实现了Set接口。
Java代码  
再来看看for/in循环的语法  
for(declaration : expression)  
statement  
  
在执行阶段被翻译成如下各式  
for(Iterator<E> #i = (expression).iterator(); #i.hashNext();){  
declaration = #i.next();  
statement  
}  
 

因此在第一个for语句for (String appFieldDefId : appFieldDefIds) 中调用了HashMap.keySet().iterator() 而这个方法调用了newKeyIterator()
Java代码  
Iterator<K> newKeyIterator() {  
return new KeyIterator();  
}  
private class KeyIterator extends HashIterator<K> {  
public K next() {  
return nextEntry().getKey();  
}  
}  
 

所以在for中还是调用了
在第二个循环for(Entry<String, String[]> entry : paraMap.entrySet())中使用的Iterator是如下的一个内部类
Java代码  
private class EntryIterator extends HashIterator<Map.Entry<K,V>> {  
public Map.Entry<K,V> next() {  
return nextEntry();  
}  
}  
 
此时第一个循环得到key,第二个循环得到HashMap的Entry
效率就是从循环里面体现出来的第二个循环此致可以直接取key和value值
而第一个循环还是得再利用HashMap的get(Object key)来取value值

现在看看HashMap的get(Object key)方法
Java代码  
public V get(Object key) {  
Object k = maskNull(key);  
int hash = hash(k);  
int i = indexFor(hash, table.length); //Entry[] table   
Entry<K,V> e = table;  
while (true) {  
if (e == null)  
return null;  
if (e.hash == hash && eq(k, e.key))   
return e.value;  
        e = e.next;  
}  
}  
 
其实就是再次利用Hash值取出相应的Entry做比较得到结果,所以使用第一中循环相当于两次进入HashMap的Entry中
而第二个循环取得Entry的值之后直接取key和value,效率比第一个循环高。其实按照Map的概念来看也应该是用第二个循环好一点,它本来就是key和value的值对,将key和value分开操作在这里不是个好选择。

27、array(数组) 和 ArryList的使用
array([]):最高效;但是其容量固定且无法动态改变;
ArrayList:容量可动态增长;但牺牲效率;
基于效率和类型检验,应尽可能使用array,无法确定数组大小时才使用ArrayList!
ArrayList是Array的复杂版本
ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。
ArrayList存入对象时,抛弃类型信息,所有对象屏蔽为Object,编译时不检查类型,但是运行时会报错。
注:jdk5中加入了对泛型的支持,已经可以在使用ArrayList时进行类型检查。
从这一点上看来,ArrayList与数组的区别主要就是由于动态增容的效率问题了

28、尽量使用HashMap 和ArrayList ,除非必要,否则不推荐使用HashTable和Vector ,后者由于使用同步机制,而导致了性能的开销。
利用jqueryRotare实现抽奖转盘 jquery http://www.cnblogs.com/mofish/archive/2013/01/24/2875516.html
利用jqueryRotare实现抽奖转盘

很多公司到了年底都会做一些抽奖活动来刺激、吸引、粘住客户,比如抽奖转盘活动。

前几天用一个jqueryRotate插件实现了转盘的效果。比起那些很炫丽的flash是稍逊点,但也基本实现了需求

效果图:(也可以看线上的效果:http://www.mbabycare.com/course/lottery):



实现这个其实蛮简单的,转动的效果用的jqueryRotate插件,所以只要判断每个奖荐对应的角度,然后设置指针的转动角度就可以了。比如关键的是jqueryRotate这个插件的用法。

jqueryRotate的资料:

支持Internet Explorer 6.0+ 、Firefox 2.0 、Safari 3 、Opera 9 、Google Chrome,高级浏览器下使用Transform,低版本ie使用VML实现

google code地址:http://code.google.com/p/jqueryrotate/

调用和方法:


$(el).rotate({  
    angle:0,  //起始角度
     animateTo:180,  //结束的角度
     duration:500, //转动时间
     callback:function(){}, //回调函数
     easing: $.easing.easeInOutExpo //定义运动的效果,需要引用jquery.easing.min.js的文件
  })

$(el).rotate(45); //直接这样子调用的话就是变换角度

$(el).getRotateAngle(); //返回对象当前的角度

$(el).stopRotare(); //停止旋转动画

另外可以更方便的通过调用$(el).rotateRight()和$(el).rotateLeft()来分别向右旋转90度和向左旋转90度。

很简单吧,各种example可以看这里:http://code.google.com/p/jqueryrotate/wiki/Examples

下面是用jqueryRotate实现的抽奖转盘页面:


<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>转盘</title>
<style>
    *{padding:0;margin:0}
    body{
        text-align:center
    }
    .ly-plate{
        position:relative;
        width:509px;
        height:509px;
        margin: 50px auto;
    }
    .rotate-bg{
        width:509px;
        height:509px;
        background:url(ly-plate.png);
        position:absolute;
        top:0;
        left:0
    }
    .ly-plate div.lottery-star{
        width:214px;
        height:214px;
        position:absolute;
        top:150px;
        left:147px;
        /*text-indent:-999em;
        overflow:hidden;
        background:url(rotate-static.png);
        -webkit-transform:rotate(0deg);*/
        outline:none
    }
    .ly-plate div.lottery-star #lotteryBtn{
        cursor: pointer;
        position: absolute;
        top:0;
        left:0;
        *left:-107px
    }
</style>
</head>
<body>
    <div class="ly-plate">
        <div class="rotate-bg"></div>
        <div class="lottery-star"><img src="rotate-static.png" id="lotteryBtn"></div>
    </div>
</body>
<script src="jquery-1.7.2.min.js"></script>
<script src="jQueryRotate.2.2.js"></script>

<script>
$(function(){
    var timeOut = function(){  //超时函数
        $("#lotteryBtn").rotate({
            angle:0, 
            duration: 10000, 
            animateTo: 2160,  //这里是设置请求超时后返回的角度,所以应该还是回到最原始的位置,2160是因为我要让它转6圈,就是360*6得来的
            callback:function(){
                alert('网络超时')
            }
        }); 
    }; 
    var rotateFunc = function(awards,angle,text){  //awards:奖项,angle:奖项对应的角度,text:提示文字
        $('#lotteryBtn').stopRotate();
        $("#lotteryBtn").rotate({
            angle:0, 
            duration: 5000, 
            animateTo: angle+1440, //angle是图片上各奖项对应的角度,1440是我要让指针旋转4圈。所以最后的结束的角度就是这样子^^
            callback:function(){
                alert(text)
            }
        }); 
    };
    
    $("#lotteryBtn").rotate({ 
       bind: 
         { 
            click: function(){
                var time = [0,1];
                    time = time[Math.floor(Math.random()*time.length)];
                if(time==0){
                    timeOut(); //网络超时
                }
                if(time==1){
                    var data = [1,2,3,0]; //返回的数组
                        data = data[Math.floor(Math.random()*data.length)];
                    if(data==1){
                        rotateFunc(1,157,'恭喜您抽中的一等奖')
                    }
                    if(data==2){
                        rotateFunc(2,247,'恭喜您抽中的二等奖')
                    }
                    if(data==3){
                        rotateFunc(3,22,'恭喜您抽中的三等奖')
                    }
                    if(data==0){
                        var angle = [67,112,202,292,337];
                            angle = angle[Math.floor(Math.random()*angle.length)]
                        rotateFunc(0,angle,'很遗憾,这次您未抽中奖')
                    }
                }
            }
         } 
       
    });
    
})
</script>
</html>

这里的time跟data是要从后台获取的,但这里只是静态页面,所以我就利用了random随机数来尽量模拟抽奖的过程了。

time参数表示从后台请求是否成功,0是请求超时,1是请求成功(成功后再判断返回的值是什么样);

data就是请求返回的数据,1,2,3表示一二三等奖,0是不中奖,根据返回的数据,再去设置指针旋转的角度。

因为这个图片上的角度无法用公式计算出来,所以只能这样子计算出来后定死。

如果比较规则的话,应该可以用公式计算。

若你有更好的想法,也可以留言给我:)

点击下载完整demo

 

参考:http://www.css88.com/archives/4959
分布式企业服务总线消息机制的研究与实现 distributed http://cdmd.cnki.com.cn/Article/CDMD-10335-2010049359.htm

        
WEB 系统多级分布式缓存机制设计与实现 distributed http://www.docin.com/p-427118343.html

        
LINUX 常用命令 linux http://www.cnblogs.com/riky/archive/2007/05/08/739337.html
su 
su命令是最基本的命令之一,常用于不同用户间切换。例如,如果登录为 user1,要切换为user2,只要用如下命令: 
$su user2 
然后系统提示输入user2口令,输入正确的口令之后就可以切换到user2。完成之后就可以用exit命令返回到user1。 
su命令的常见用法是变成根用户或超级用户。如果发出不带用户名的su命令 ,则系统提示输入根口令,输入之后则可切换为根用户。 
如果登录为根用户,则可以用su命令成为系统上任何用户而不需要口令。

pwd 
pwd命令也是最常用最基本的命令之一,用于显示用户当前所在的目录。

cd 
cd命令不仅显示当前状态,还改变当前状态,它的用发跟dos下的cd命令基本一致。 
cd ..可进入上一层目录 
cd -可进入上一个进入的目录 
cd ~可进入用户的home目录

ls 
ls命令跟dos下的dir命令一样,用于显示当前目录的内容。 
如果想取得详细的信息,可用ls -l命令, 这样就可以显示目录内容的详细信息。 
如果目录下的文件太多,用一屏显示不了,可以用ls -l |more分屏显示 。

find 
find命令用于查找文件。这个命令可以按文件名、建立或修改日期、所有者(通常是建立文件的用户)、文件长度或文件类型进行搜索。 
find命令的基本结构如下: 
$find 
其中指定从哪个目录开始搜索。指定搜索条件。表示找到文件怎么处理。一般来说,要用-print动作,显示 整个文件路径和名称。如果没有这个动作,则find命令进行所要搜索而不显示结果,等于白费劲。 
例如,要搜索系统上所有名称为ye的文件,可用如下命令: 
$find / -name ye -print 
这样就可以显示出系统上所有名称为ye的文件。

tar 
tar最初用于建立磁带备份系统,目前广泛用于建立文件发布档案。可用如下方法建立tar档案: 
$tar cvf 
例如,如果要将当前目录中所有文件存档到ye.tar中,可用如下命令: 
$tar cvf ye.tar *.* 
要浏览档案内容,将c选项变成t。如果要浏览ye.tar档案中的内容,可用如下命令: 
$tar tvf ye.tar 
要取出档案内的内容,将c选项变成x。如果要将ye.tar档案中的内容取到当前目录中,可用如下命令: 
$tar xvf ye.tar

gzip 
gzip命令用于压缩文件。 例如,如果要将ye.txt文件压缩,可用如下命令: 
$gzip ye.txt 
这样就可以压缩文件并在文件名后面加上gz扩展名,变成文件ye.txt.gz。 
解压缩文件可用gzip -d命令实现: 
$gzip -d ye.txt.gz 
这样就可以解压缩文件并删除gz扩展名。除此之外还可以用gunzip命令来解 压缩文件,效果跟用gzip -d命令一样。 
旧版的tar命令不压缩档案,可用gzip压缩。例如: 
$tar cvf ye.tar *.txt 
$gzip ye.tar 
则可建立压缩档案ye.tar.gz。 
新版的tar可以直接访问和建立gzip压缩的tar档案,只要在tar命令中加上z 选项就可以了。例如: 
$tar czvf ye.tar *.txt 
生成压缩档案ye.tar.gz, 
$tar tzvf ye.tar *.txt 
显示压缩档案ye.tar.gz的内容,而 
$tar xzvf ye.tar *.txt 
取出压缩档案ye.tar.gz的内容。

mkdir 
这个命令很简单,跟dos的md命令用法几乎一样,用于建立目录。

cp 
cp命令用于复制文件或目录。 
cp命令可以一次复制多个文件,例如: 
$cp *.txt *.doc *.bak /home 
将当前目录中扩展名为txt、doc和bak的文件全部复制到/home目录中。 
如果要复制整个目录及其所有子目录,可以用cp -R命令。

rm 
rm命令用于删除文件或目录。 
rm命令会强制删除文件,如果想要在删除时提示确认,可用rm -i命令。 
如果要删除目录,可用rm -r命令。rm -r命令在删除目录时,每删除一个文件或目录都会显示提示,如果目录太大,响应每个提示是不现实的。这时可以用 rm -rf命令来强制删除目录,这样即使用了-i标志也当无效处理。

mv 
mv命令用于移动文件和更名文件。例如: 
$mv ye.txt /home 
将当前目录下的ye.txt文件移动到/home目录下, 
$mv ye.txt ye1.txt 
将ye.txt文件改名为ye1.txt。 
类似于跟cp命令,mv命令也可以一次移动多个文件,在此不再赘叙。

reboot

 

◆ 安装和登录命令:login、shutdown、halt、reboot、install、mount、umount、chsh、exit、last; 

◆ 文件处理命令:file、mkdir、grep、dd、find、mv、ls、diff、cat、ln; 

◆ 系统管理相关命令:df、top、free、quota、at、lp、adduser、groupadd、kill、crontab; 

◆ 网络操作命令:ifconfig、ip、ping、netstat、telnet、ftp、route、rlogin、rcp、finger、mail、 nslookup; 

◆ 系统安全相关命令:passwd、su、umask、chgrp、chmod、chown、chattr、sudo ps、who; 

◆ 其它命令:tar、unzip、gunzip、unarj、mtools、man、unendcode、uudecode。 

本文以Mandrake Linux 9.1(Kenrel 2.4.21)为例,介绍Linux下的安装和登录命令。 

login 

1.作用 

login的作用是登录系统,它的使用权限是所有用户。 

2.格式 

login [name][-p ][-h 主机名称]

3.主要参数 

-p:通知login保持现在的环境参数。 

-h:用来向远程登录的之间传输用户名。 

如果选择用命令行模式登录Linux的话,那么看到的第一个Linux命令就是login:。 

一般界面是这样的: 

Manddrake Linux release 9.1(Bamboo) for i586 
renrel 2.4.21-0.13mdk on i686 / tty1
localhost login:root
password:

上面代码中,第一行是Linux发行版本号,第二行是内核版本号和登录的虚拟控制台,我们在第三行输入登录名,按“Enter”键在Password后输入账户密码,即可登录系统。出于安全考虑,输入账户密码时字符不会在屏幕上回显,光标也不移动。 

登录后会看到下面这个界面(以超级用户为例): 

[root@localhost root]#
last login:Tue ,Nov 18 10:00:55 on vc/1

上面显示的是登录星期、月、日、时间和使用的虚拟控制台。 

4.应用技巧 

Linux是一个真正的多用户操作系统,可以同时接受多个用户登录,还允许一个用户进行多次登录。这是因为Linux和许多版本的Unix一样,提供了虚拟控制台的访问方式,允许用户在同一时间从控制台(系统的控制台是与系统直接相连的监视器和键盘)进行多次登录。每个虚拟控制台可以看作是一个独立的工作站,工作台之间可以切换。虚拟控制台的切换可以通过按下Alt键和一个功能键来实现,通常使用F1-F6 。 

例如,用户登录后,按一下“Alt+F2”键,用户就可以看到上面出现的“login:”提示符,说明用户看到了第二个虚拟控制台。然后只需按“Alt+F1”键,就可以回到第一个虚拟控制台。 一个新安装的Linux系统允许用户使用“Alt+F1”到“Alt+F6”键来访问前六个虚拟控制台。虚拟控制台最有用的是,当一个程序出错造成系统死锁时,可以切换到其它虚拟控制台工作,关闭这个程序。 

shutdown 

1.作用 

shutdown命令的作用是关闭计算机,它的使用权限是超级用户。 

2.格式 

shutdown [-h][-i][-k][-m][-t]

3.重要参数 

-t:在改变到其它运行级别之前,告诉init程序多久以后关机。 

-k:并不真正关机,只是送警告信号给每位登录者。 

-h:关机后关闭电源。 

-c:cancel current process取消目前正在执行的关机程序。所以这个选项当然没有时间参数,但是可以输入一个用来解释的讯息,而这信息将会送到每位使用者。 

-F:在重启计算机时强迫fsck。 

-time:设定关机前的时间。 

-m: 将系统改为单用户模式。 

-i:关机时显示系统信息。 

4.命令说明 

shutdown命令可以安全地将系统关机。有些用户会使用直接断掉电源的方式来关闭Linux系统,这是十分危险的。因为Linux与Windows不同,其后台运行着许多进程,所以强制关机可能会导致进程的数据丢失,使系统处于不稳定的状态,甚至在有的系统中会损坏硬件设备(硬盘)。在系统关机前使用shutdown命令,系统管理员会通知所有登录的用户系统将要关闭,并且login指令会被冻结,即新的用户不能再登录。 

halt 

1.作用 

halt命令的作用是关闭系统,它的使用权限是超级用户。 

2.格式 

halt [-n] [-w] [-d] [-f] [-i] [-p]

3.主要参数说明 

-n:防止sync系统调用,它用在用fsck修补根分区之后,以阻止内核用老版本的超级块覆盖修补过的超级块。 

-w:并不是真正的重启或关机,只是写wtmp(/var/log/wtmp)纪录。 

-f:没有调用shutdown,而强制关机或重启。 

-i:关机(或重启)前,关掉所有的网络接口。 

-f:强迫关机,不呼叫shutdown这个指令。 

-p: 当关机的时候顺便做关闭电源的动作。 

-d:关闭系统,但不留下纪录。  

4.命令说明 

halt就是调用shutdown -h。halt执行时,杀死应用进程,执行sync(将存于buffer中的资料强制写入硬盘中)系统调用,文件系统写操作完成后就会停止内核。若系统的运行级别为0或6,则关闭系统;否则以shutdown指令(加上-h参数)来取代。  

reboot 

1.作用 

reboot命令的作用是重新启动计算机,它的使用权限是系统管理者。 

2.格式 

reboot [-n] [-w] [-d] [-f] [-i]

3.主要参数 

-n: 在重开机前不做将记忆体资料写回硬盘的动作。 

-w: 并不会真的重开机,只是把记录写到/var/log/wtmp文件里。 

-d: 不把记录写到/var/log/wtmp文件里(-n这个参数包含了-d)。 

-i: 在重开机之前先把所有与网络相关的装置停止。 

install 

1.作用 

install命令的作用是安装或升级软件或备份数据,它的使用权限是所有用户。 

2.格式 

(1)install [选项]... 来源 目的地 

(2)install [选项]... 来源... 目录 

(3)install -d [选项]... 目录... 

在前两种格式中,会将<来源>复制至<目的地>或将多个<来源>文件复制至已存在的<目录>,同时设定权限模式及所有者/所属组。在第三种格式中,会创建所有指定的目录及它们的主目录。长选项必须用的参数在使用短选项时也是必须的。 

3.主要参数 

--backup[=CONTROL]:为每个已存在的目的地文件进行备份。 

-b:类似 --backup,但不接受任何参数。 

-c:(此选项不作处理)。 

-d,--directory:所有参数都作为目录处理,而且会创建指定目录的所有主目录。 

-D:创建<目的地>前的所有主目录,然后将<来源>复制至 <目的地>;在第一种使用格式中有用。 

-g,--group=组:自行设定所属组,而不是进程目前的所属组。 

-m,--mode=模式:自行设定权限模式 (像chmod),而不是rwxr-xr-x。 

-o,--owner=所有者:自行设定所有者 (只适用于超级用户)。 

-p,--preserve-timestamps:以<来源>文件的访问/修改时间作为相应的目的地文件的时间属性。 

-s,--strip:用strip命令删除symbol table,只适用于第一及第二种使用格式。 

-S,--suffix=后缀:自行指定备份文件的<后缀>。 

-v,--verbose:处理每个文件/目录时印出名称。 

--help:显示此帮助信息并离开。 

--version:显示版本信息并离开。 

mount 

1.作用 

mount命令的作用是加载文件系统,它的用权限是超级用户或/etc/fstab中允许的使用者。 

2.格式 

mount -a [-fv] [-t vfstype] [-n] [-rw] [-F] device dir

3.主要参数 

-h:显示辅助信息。 

-v:显示信息,通常和-f用来除错。 

-a:将/etc/fstab中定义的所有文件系统挂上。 

-F:这个命令通常和-a一起使用,它会为每一个mount的动作产生一个行程负责执行。在系统需要挂上大量NFS文件系统时可以加快加载的速度。 

-f:通常用于除错。它会使mount不执行实际挂上的动作,而是模拟整个挂上的过程,通常会和-v一起使用。 

-t vfstype:显示被加载文件系统的类型。 

-n:一般而言,mount挂上后会在/etc/mtab中写入一笔资料,在系统中没有可写入文件系统的情况下,可以用这个选项取消这个动作。 

4.应用技巧 

在Linux和Unix系统上,所有文件都是作为一个大型树(以/为根)的一部分访问的。要访问CD-ROM上的文件,需要将CD-ROM设备挂装在文件树中的某个挂装点。如果发行版安装了自动挂装包,那么这个步骤可自动进行。在Linux中,如果要使用硬盘、光驱等储存设备 ,就得先将它加载,当储存设备挂上了之后,就可以把它当成一个目录来访问。挂上一个设备使用mount命令。 在使用mount这个指令时,至少要先知道下列三种信息:要加载对象的文件系统类型、要加载对象的设备名称及要将设备加载到哪个目录下。 

(1)Linux可以识别的文件系统 

◆ Windows 95/98常用的FAT 32文件系统:vfat ; 

◆ Win NT/2000 的文件系统:ntfs ; 

◆ OS/2用的文件系统:hpfs; 

◆ Linux用的文件系统:ext2、ext3; 

◆ CD-ROM光盘用的文件系统:iso9660。 

虽然vfat是指FAT 32系统,但事实上它也兼容FAT 16的文件系统类型。 

(2)确定设备的名称 

在Linux中,设备名称通常都存在/dev里。这些设备名称的命名都是有规则的,可以用“推理”的方式把设备名称找出来。例如,/dev/hda1这个IDE设备,hd是Hard Disk(硬盘)的,sd是SCSI Device,fd是Floppy Device(或是Floppy Disk?)。a代表第一个设备,通常IDE接口可以接上4个IDE设备(比如4块硬盘)。所以要识别IDE硬盘的方法分别就是hda、hdb、hdc、hdd。hda1中的“1”代表hda的第一个硬盘分区 (partition),hda2代表hda的第二主分区,第一个逻辑分区从hda5开始,依此类推。 此外,可以直接检查/var/log/messages文件,在该文件中可以找到计算机开机后系统已辨认出来的设备代号。 

(3)查找挂接点 

在决定将设备挂接之前,先要查看一下计算机是不是有个/mnt的空目录,该目录就是专门用来当作挂载点(Mount Point)的目录。建议在/mnt里建几个/mnt/cdrom、/mnt/floppy、/mnt/mo等目录,当作目录的专用挂载点。举例而言,如要挂载下列5个设备,其执行指令可能如下 (假设都是Linux的ext2系统,如果是Windows XX请将ext2改成vfat): 

软盘 ===>mount -t ext2 /dev/fd0 /mnt/floppy 
cdrom ===>mount -t iso9660 /dev/hdc /mnt/cdrom 
SCSI cdrom ===>mount -t iso9660 /dev/sdb /mnt/scdrom 
SCSI cdr ===>mount -t iso9660 /dev/sdc /mnt/scdr

不过目前大多数较新的Linux发行版本(包括红旗 Linux、中软Linux、Mandrake Linux等)都可以自动挂装文件系统,但Red Hat Linux除外。 

umount 

1.作用 

umount命令的作用是卸载一个文件系统,它的使用权限是超级用户或/etc/fstab中允许的使用者。 

2.格式 

unmount -a [-fFnrsvw] [-t vfstype] [-n] [-rw] [-F] device dir

3.使用说明 

umount命令是mount命令的逆操作,它的参数和使用方法和mount命令是一样的。Linux挂装CD-ROM后,会锁定CD—ROM,这样就不能用CD-ROM面板上的Eject按钮弹出它。但是,当不再需要光盘时,如果已将/cdrom作为符号链接,请使用umount/cdrom来卸装它。仅当无用户正在使用光盘时,该命令才会成功。该命令包括了将带有当前工作目录当作该光盘中的目录的终端窗口。 

chsh 

1.作用 

chsh命令的作用是更改使用者shell设定,它的使用权限是所有使用者。 

2.格式 

chsh [ -s ] [ -list] [ --help ] [ -v ] [ username ]

3.主要参数 

-l:显示系统所有Shell类型。 

-v:显示Shell版本号。 

4.应用技巧 

前面介绍了Linux下有多种Shell,一般缺省的是Bash,如果想更换Shell类型可以使用chsh命令。先输入账户密码,然后输入新Shell类型,如果操作正确系统会显示“Shell change”。其界面一般如下: 

Changing fihanging shell for cao
Password: 
New shell [/bin/bash]: /bin/tcsh

上面代码中,[ ]内是目前使用的Shell。普通用户只能修改自己的Shell,超级用户可以修改全体用户的Shell。要想查询系统提供哪些Shell,可以使用chsh -l 命令,见图1所示。 


图1 系统可以使用的Shell类型 


从图1中可以看到,笔者系统中可以使用的Shell有bash(缺省)、csh、sh、tcsh四种。 

exit 

1.作用 

exit命令的作用是退出系统,它的使用权限是所有用户。 

2.格式 

exit 

3.参数 

exit命令没有参数,运行后退出系统进入登录界面。 

last 

1.作用 

last命令的作用是显示近期用户或终端的登录情况,它的使用权限是所有用户。通过last命令查看该程序的log,管理员可以获知谁曾经或企图连接系统。 

2.格式 

1ast[—n][-f file][-t tty] [—h 节点][-I —IP][—1][-y][1D]

3.主要参数 

-n:指定输出记录的条数。 

-f file:指定用文件file作为查询用的log文件。 

-t tty:只显示指定的虚拟控制台上登录情况。 

-h 节点:只显示指定的节点上的登录情况。 

-i IP:只显示指定的IP上登录的情况。 

-1:用IP来显示远端地址。 

-y:显示记录的年、月、日。 

-ID:知道查询的用户名。 

-x:显示系统关闭、用户登录和退出的历史。 

动手练习 

上面介绍了Linux安装和登录命令,下面介绍几个实例,动手练习一下刚才讲过的命令。 

1.一次运行多个命令 

在一个命令行中可以执行多个命令,用分号将各个命令隔开即可,例如: 

#last -x;halt

上面代码表示在显示系统关闭、用户登录和退出的历史后关闭计算机。 

2.利用mount挂装文件系统访问Windows系统 

许多Linux发行版本现在都可以自动加载Vfat分区来访问Windows系统,而Red Hat各个版本都没有自动加载Vfat分区,因此还需要进行手工操作。 

mount可以将Windows分区作为Linux的一个“文件”挂接到Linux的一个空文件夹下,从而将Windows的分区和/mnt这个目录联系起来。因此,只要访问这个文件夹就相当于访问该分区了。首先要在/mnt下建立winc文件夹,在命令提示符下输入下面命令: 

#mount -t vfat /dev/hda1 /mnt/winc

即表示将Windows的C分区挂到Liunx的/mnt/winc目录下。这时,在/mnt/winc目录下就可以看到Windows中C盘的内容了。使用类似的方法可以访问Windows系统的D、E盘。在Linux系统显示Windows的分区一般顺序这样的:hda1为C盘、hda5为D盘、hda6为E盘……以此类推。上述方法可以查看Windows系统有一个很大的问题,就是Windows中的所有中文文件名或文件夹名全部显示为问号“?”,而英文却可以正常显示。我们可以通过加入一些参数让它显示中文。还以上面的操作为例,此时输入命令: 

#mount -t vfat -o iocharset=cp936 /dev/hda1 /mnt/winc

现在它就可以正常显示中文了。 

3.使用mount加挂闪盘上的文件系统 

在Linux下使用闪盘非常简单。Linux对USB设备有很好的支持,当插入闪盘后,闪盘被识别为一个SCSI盘,通常输入以下命令: 

# mount /dev/sda1 /usb

就能够加挂闪盘上的文件系统。 

小知识 

Linux命令与Shell 

所谓Shell,就是命令解释程序,它提供了程序设计接口,可以使用程序来编程。学习Shell对于Linux初学者理解Linux系统是非常重要的。Linux系统的Shell作为操作系统的外壳,为用户提供了使用操作系统的接口。Shell是命令语言、命令解释程序及程序设计语言的统称,是用户和Linux内核之间的接口程序。如果把Linux内核想象成一个球体的中心,Shell就是围绕内核的外层。当从Shell或其它程序向Linux传递命令时,内核会做出相应的反应。Shell在Linux系统的作用和MS DOS下的COMMAND.COM和Windows 95/98 的 explorer.exe相似。Shell虽然不是系统核心的一部分,只是系统核心的一个外延,但它能够调用系统内核的大部分功能。因此,可以说Shell是Unux/Linux最重要的实用程序。 

Linux中的Shell有多种类型,其中最常用的是Bourne Shell(sh)、C Shell(csh)和Korn Shell(ksh)。大多数Linux发行版本缺省的Shell是Bourne Again Shell,它是Bourne Shell的扩展,简称bash,与Bourne Shell完全向后兼容,并且在Bourne Shell的基础上增加了很多特性。bash放在/bin/bash中,可以提供如命令补全、命令编辑和命令历史表等功能。它还包含了很多C Shell和Korn Shell中的优点,有灵活和强大的编程接口,同时又有很友好的用户界面。Linux系统中200多个命令中有40个是bash的内部命令,主要包括exit、less、lp、kill、 cd、pwd、fc、fg等
jbpm入门 jbpm http://blog.csdn.net/hxirui/article/details/1221911
http://blog.csdn.net/hxirui/article/details/1221911
在spring中使用velocity,freemaker velocity http://hi.baidu.com/coolcat_police/item/04b05028726f208a6e2cc3d1
需要用到的jar包:spring-webmvc-2.5.jar commons-logging-1.1.jar commons-collections-3.1.jar commons-lang-2.4.jar spring-2.5.jar velocity-1.5.jar
1.配置web.xml
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcher</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>
在WEB-INF下面创建dispatcher-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd       
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
    
    <!-- ①:对controller包中的所有类进行扫描,以完成Bean创建和自动依赖注入的功能 -->
    <context:component-scan base-package="controller"/>
    <!-- ②:启动Spring MVC的注解功能,完成请求和注解POJO的映射 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
    <!-- ③:对模型视图名称的解析,即在模型视图名称添加前后缀 -->
<bean id="viewResolver"
   class="org.springframework.web.servlet.view.velocity.VelocityLayoutViewResolver">
   <property name="viewClass"
    value="org.springframework.web.servlet.view.velocity.VelocityLayoutView" />
   <property name="contentType" value="text/html;charset=UTF-8" />
   <property name="exposeSpringMacroHelpers" value="true" />
   <property name="prefix" value="/velocity/" />
   <property name="suffix" value=".vm" />
   <property name="toolboxConfigLocation" value="/velocity/velocity-toolbox.xml" />
   <property name="layoutUrl" value="/velocity/myLayout.vm" />
   <property name="layoutKey" value="layout" />
   <property name="screenContentKey" value="screen_content" />
</bean>
<bean id="velocityConfigurer"
   class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
   <property name="resourceLoaderPath" value="/" />
   <property name="configLocation" value="/velocity/velocity.properties" />
</bean>
    
</beans>
另外,freemaker的配置也相似:
    <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"
        p:prefix="/WEB-INF/freemaker/" p:suffix=".ftl"/>
    <bean id="freemakerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
        <property name="templateLoaderPath" value="/"/>
    </bean>    
    
    <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"/>
测试:
在WEB-INF/velocity下新建myvm.vm:
<html>   
<head><title>myvm</title></head>   
<body>    
$username
,welcome!
</body>   
</html>
然后在源代码根目录新建controller包,在此包下创建ToVM类:
@Controller
public class ToVM {
    @RequestMapping("/toMyvm.htm")
    public String toMyvm(ModelMap map){
        map.addAttribute("username","Jason");
        return "myvm";
    }
}
部署项目后,在浏览器输入http://localhost:8080/springVelocityTest/toMyvm.htm
就会看到Jason,welcome!
模板技术velocity和freemarker的比较 velocity http://llh1985.blog.163.com/blog/static/89658031201052513610872/
模板技术在现代的软件开发中有着重要的地位,而目前最流行的两种模板技术恐怕要算freemarker和velocity了,webwork2.2对两者都有不错的支持,也就是说在webwork2中你可以随意选择使用freemarker或velocity作为view,模板技术作为view的好处是很多,尤其和jsp比较起来优点更大,众所周知jsp需要在第一次被执行的时候编译成servlet,那么这个过程是很慢的,当然很多应用服务器都提供预编译的功能,但是在开发的时候仍然给我们程序员带来了很多痛苦,每次修改都要多几秒钟,那在一天的开发中就有很多时间浪费在jsp的编译上了。用webwork in action的作者的话来说:“每次修改之后重新运行都要等等几秒是令人失望的,而频繁地修改jsp更是会令你的失望情绪变本加厉“。我们把模板技术引入到view中去可以带来更好的开发效率,而且模板的速度要比jsp快(虽然编译过后的jsp在速度上已经满足我的需求了,呵呵)。 当然模板技术可以用在很多领域,可不只在view那里。我们可以通过模板技术来生成xml,生成jsp,生成java文件等等,说到这里,大家通常会使用模板技术用在公司的框架里,这样就可以很快速的生成添删改查的代码,需要的只是模板,其他比如还有邮件模板等等。 

以上是模板的作用,当然模板还有其他领域的应用,希望能和大家多讨论,提高我们的生产效率。 

那么现在开源的模板技术有好几种,多了之后就有一个选择的问题了,如何选择一个满足自己需要的模板的呢,我大概了看了一下两种模板技术,写了一个例子,我使用了几种设计模式来完成了这个例子,这个例子中,我同时使用了freemarker和velocity,这样同学们可以通过代码很直观的比较两种模板技术,通过这个例子,我认识到freemarker在功能上要比velocity强大 
1在view层的时候,它提供了format日期和数字的功能,我想大家都有在页面上format日期或数字的经验,用jsp的同学可能对jstl的fmt标签很有感情,使用了freemarker之后也可以使用freemarker提供的功能来formmat日期和数据,这个功能我想是很贴心的 

2通过我的使用我发现freemaker的eclipseplugin要比velocity的eclipseplugin好,如果你是用idea那很遗憾,我没有找到类似的插件。好在很多地方呢,我看到的是freemarker的插件除了支持freemarker语法也支持html语句,而velocity的插件貌似只支持velocity的语法,html就只是用普通的文本来显示了,在这一点上freemarker占上风了(不要和我说高手都是用windows记事本之类的话,这本来就违背了模板技术的初衷) 

3freemarker对jsptag的支持很好,算了,不到迫不得已还是不要这样做吧。 

还有就是两者的语法格式,这一点上不同的人有不同倾向

由于网易博客没用代码插入功能,所以例子就不转载了,需要的朋友去原文http://mamacmm.javaeye.com/blog/485332看吧,有下载

 

二,抄录于http://southking.javaeye.com/blog/235920

Velocity和FreeMarker优缺点
Velocity优点: 
采用简单而强大的模板语言VTL来渲染页面,能保证在Dreamwaver之类的可视化编辑器中正常显示。 
模板可以是任意扩展名,采用.html也可以,这样就能直接在浏览器中看到效果。 
渲染速度快。 
Velocity缺点: 
不是JavaEE标准,文档较少。 
VTL语法需要一定上的学习时间,尽管相对较容易。 
FreeMarker优点: 
Struts2默认采用(是否溶合起来比Velocity更容易?) 
具有一些编程能力,虽然有限,只能提供一些数据格式的轮换功能。 
与WEB容器无关,除了HTML,也可以生成各种文本,如XML、RTF、Java源代码等。 
FreeMarker缺点: 
freemarker的map限定key必须是string,不支持其他数据类型。 
freemarker的变量必须有值,没有被赋值的变量就会抛出异常。程序里面几乎所有可能出现空值的变量统统需要加上${xxx?if_exists},有些循环条件还需要写if判断。 
不能在群集上面发布应用。freemarker支持在页面里面直接操作Session,request等,例如${Session[...]},但不能对其序列化。

 

 

三,转载于http://www.zzbaike.com/wiki/Velocity%E4%B8%8EFreeMarker

Velocity与FreeMarker

相比较FreeMarker而言,Velocity更加简单、轻量级,但它的功能却没有FreeMarker那么强大。依据评测的结论, freemarker 稍快一些。差别在毫秒级别,对用户而言可以忽略不计,若超大流量,考虑负载时,较倾向后者。

velocity 出现得较早些,也有更多的受众,因此在考虑到人员配给上,略占优势。但是模板技术上手都很快的,所以无所谓。

对于大部分的应用来说,使用 FreeMarker 比 Velocity 更简单,因为 Velocity 还必须编写一些自定义的toolbox类以及一遍遍重复的编写一些比较通用的模版代码,因此也就丧失了刚开始开发时更多的宝贵时间。另外使用工具类和变通的方法在模版引擎中似乎不是一个非常有效的做法。同时,Velocity 的做法使得在Velocity的模版中大量的跟 Java 对象进行交互,这违反了简单的原则,尽管你也可以将代码转入控制器中实现。当然,如果你像使用 Velocity 一样来使用 FreeMarker ,那么 FreeMarker 也可以跟 Velocity 一样简单。

Velocity 一个优于FreeMarker的地方在于它有很广泛的第三方支持以及一个非常庞大的用户社区,你可以通过这个社区获得到很多的帮助,相反的FreeMarker在这方面要差很多。当然,也有越来越多的第三方软件开始在支持 FreeMarker 。

下面是一些 FreeMarker 能做到的,而 Velocity 做不到的功能列表(且看着):

日期和数字的支持
您可以执行运算和比较,对任意数量的类型,包括任意精度类型,而不仅仅是整数。您可以比较和显示(格式化)日期/时间值。

国际化
您可以格式数字区域,各种各样的内置和自定义数字格式模式。您可以格式日期地区和时区,各种各样的内置和定制的日期格式模式。标识符(变量名)可以包含非英语字母一样重音字母,阿拉伯字母,汉字等

循环处理
您可以退出循环您可以访问控制变量外循环机构的内部循环您可以得知当前是否到了循环的结束位置

模版级别的数组处理
您可以使用[i]的语法来访问数组元素,包括原始的和非原始的指数可以获取到数组的长度

宏定义
宏调用可以通过位置或名称进行参数传递宏的参数可以设定默认值,在调用宏时如果没有指定该参数,则使用默认值代替通过 <@myMacro>body</@myMacro> 可以支持宏的嵌套可以通过文本表达的“宏的名称”来直接调用某个宏宏允许先使用再定义宏可以定义局部变量(新版本的Velocity也通过#local指令来实现该功能,尽管官方的文档还没有进行介绍)

命名空间
您可以使用多个名称空间的变数。当您建立“宏库”时是非常有用的 ,因为可以防止名称冲突与申请特定变量或与其他宏变量的库。

内置与 Java 语言无关的字符串、列表、Map 的操作方法
能提示模版中的拼写错误以及其他错误
当访问一个不存在的变量时,FreeMarker 在执行该模版时会报错,通过配置,你可以指定 FreeMarker 在碰到此类错误时是停止执行,还是忽略该错误,同时 FreeMarker 会在日志中记录此问题;如果您输入错误指令的名称,FreeMarker将抛出一个异常。

更高级的文本输出工具


文本处理
支持Java的特殊字符处理,例如\b, \t, \n, \f, \r, \", \', \\,以及UNICODE的\xXXXX 除了通常的字符串,数字,和布尔常量您可以定义列表和地图文字以及内部模板

高级的空格清除
FreeMarker 将删除一些多余的空格、跳格、换行等字符,从而消除一些令人厌烦的明显多余的空格 FreeMarker 也提供指令来删除多于的空格

与其他技术的集成
提供JSP 标签库以便在 JSP 中嵌入 FreeMarker 模版可以直接跟 Python 对象一起工作

更强大的XML转换功能
先进的模板元程序
您可以捕捉到输出的任意部分范本背景变量您可以任意解释的范围变量,就好像它是一个模板定义
Global site tag (gtag.js) - Google Analytics