Posts Tagged ‘ java

Java循环遍历不定长数组链表

功能描述
一个链表中保存多个不同长度的数组, 根据一个索引值, 能取到对应的元素所在的数组下标

public static int[] calcIndexArr(final List<String[]> list, int index) {
    int listSize;
    if (list == null || (listSize = list.size()) == 0) {
        return new int[0];
    }

    int max = 1;
    for (int i = 0; i < list.size(); i++) {
        max *= list.get(i).length;
    }
    while (index >= max) {
        index -= max;
    }

    int[] indexArr = new int[list.size()];

    for (int i = 0; i < listSize; i++) {
        int b = 1;
        for (int j = i + 1; j < listSize; j++) {
            b *= list.get(j).length;
        }
        int a = index / b;
        indexArr[i] = a;
        index = index - a * b;
    }
    return indexArr;
}

测试方法:

public void testCalcIndexArr() {
    List<String[]> list = new ArrayList<String[]>();
    list.add(new String[2]);
    list.add(new String[4]);
    list.add(new String[1]);
    for (int i = 0; i < 30; i++) {
        int arr[] = RandName.calcIndexArr(list, i);
        assertEquals(3, arr.length);
        System.out.println(arr[0] + "," + arr[1] + "," + arr[2]);
    }
}

测试结果

0,0,0
0,1,0
0,2,0
0,3,0
1,0,0
1,1,0
1,2,0
1,3,0
0,0,0
0,1,0
0,2,0
0,3,0
1,0,0
1,1,0
1,2,0
1,3,0
0,0,0
0,1,0
0,2,0
0,3,0
1,0,0
1,1,0
1,2,0
1,3,0
0,0,0
0,1,0
0,2,0
0,3,0
1,0,0
1,1,0

解决Jsp插入Mysql,设置UTF8依然乱码问题

问题描述:
JSP网页里是charset=UTF-8: 

<%@ page contentType= “text/html; charset=UTF-8 ” language= “java ” import= “java.sql.* ” errorPage= “error.jsp ” %> 

Mysql5里存取的数据库的charset也设成了UTF-8,可是通过JSP网页存取中文还是显示乱码 

解决方案:加个过滤类 

package com.bx.util; 

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; 

public class SetEncodingFilter implements Filter { 

    // ----------------------------------------------------- Instance  Variables 

    /**
     * The default character encoding to set for requests that pass through this
     * filter.
     */
    protected String encoding = null; 

    /**
     * The filter configuration object we are associated with. If this value is
     * null, this filter instance is not currently configured.
     */
    protected FilterConfig filterConfig = null; 

    /**
     * Should a character encoding specified by the client be ignored?
     */
    protected boolean ignore = true; 

    // --------------------------------------------------------- Public Methods 

    /**
     * Take this filter out of service.
     */
    public void destroy() {
        this.encoding = null;
        this.filterConfig = null;
    } 

    /**
     * Select and set (if specified) the character encoding to be used to
     * interpret request parameters for this request.
     *
     * @param request
     * The servlet request we are processing
     * @param result
     * The servlet response we are creating
     * @param chain
     * The filter chain we are processing
     *
     * @exception IOException
     * if an input/output error occurs
     * @exception ServletException
     * if a servlet error occurs
     */
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        // Conditionally select and set the character encoding to be used
        if (ignore || (request.getCharacterEncoding() == null)) {
            String encoding = selectEncoding(request);
            if (encoding != null) {
                request.setCharacterEncoding(encoding);
            }
        } 

        // Pass control on to the next filter
        chain.doFilter(request, response);
    } 

    /**
     * Place this filter into service.
     *
     * @param filterConfig
     * The filter configuration object
     */
    public void init(FilterConfig filterConfig) throws ServletException { 

        this.filterConfig = filterConfig;
        this.encoding = filterConfig.getInitParameter("encoding");
        String value = filterConfig.getInitParameter("ignore");
        if (value == null) {
            this.ignore = true;
        } else if (value.equalsIgnoreCase("true")) {
            this.ignore = true;
        } else if (value.equalsIgnoreCase("yes")) {
           this.ignore = true;
        } else {
           this.ignore = false;
        }
    } 

    // ------------------------------------------------------ Protected Methods 

    /**
     * Select an appropriate character encoding to be used, based on the
     * characteristics of the current request and/or filter initialization
     * parameters. If no character encoding should be set, return
     * <code>null</code>.
     * <p>
     * The default implementation unconditionally returns the value configured
     * by the <strong>encoding</strong> initialization parameter for this
     * filter.
     *
     * @param request
     * The servlet request we are processing
     */
    protected String selectEncoding(ServletRequest request) {
        return (this.encoding);
    }
}

然后在web.xml文件中配置一下就可以了 

<filter>
    <filter-name>Set Character Encoding</filter-name>
    <filter-class>com.bx.util.SetEncodingFilter类的路径</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Set Character Encoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

测试PreparedStatement的批处理效率

今天研究了一下Java中PreparedStatement的批处理能力.

测试环境如下:

jdk1.6.0_21 +Mysql 5.1

Core 2 Duo T5470 (1.60GHz) + 3G内存

测试的是实现最简单的插入:

INSERT INTO `testdb` (`id`, `value`) VALUES (123456, ‘http://www.pptv.com’)

共插入20万条数据, 每个测试前都把数据库清空, 然后执行插入, 从开始插入进行计时.每项测试跑3次取平均值.

分为两项测试: 1个是不使用批处理, 逐条插入; 一个是使用addBatch(), 分别按50, 100, 500, 1000, 5000, 10000, 50000, 100000的划分方式进行批量插入. 关键代码如下:

逐行插入:

for (int times = 0; times &lt; TEST_TIMES; times++) {
    cleanDB();
    conn = DBMgr.getDBconn(&quot;testdb&quot;);
    long starttime = System.currentTimeMillis();
    for (int i = 0; i &lt; MAX_COUNT; i++) {
        pstmt = conn.prepareStatement(SQL);
        pstmt.executeUpdate();
        pstmt.close();
        System.out.println(&quot;working: &quot; + i + &quot;/&quot; + MAX_COUNT);
    }
    long endtime = System.currentTimeMillis();
    LOG.info(&quot;第&quot; + (times + 1) + &quot;次测试, 耗时&quot; + (endtime - starttime) + &quot;ms&quot;);
    alltime += endtime - starttime;
}
LOG.info(&quot;平均耗时: &quot; + (alltime / TEST_TIMES) + &quot;ms&quot;);

批处理操作

for (int times = 0; times &lt; TEST_TIMES; times++) {
    cleanDB();
    conn = DBMgr.getDBconn(&quot;testdb&quot;);
    conn.setAutoCommit(false);
    int num = 0;
    long starttime = System.currentTimeMillis();
    pstmt = conn.prepareStatement(SQL);
    for (int i = 0; i &lt; MAX_COUNT; i++) {
        pstmt.addBatch();
        num++;
        if (num % count == 0 || num == MAX_COUNT) {
            pstmt.executeBatch();
            pstmt.clearBatch();
            pstmt.close();
            pstmt = conn.prepareStatement(SQL);
        }
        System.out.println(&quot;working: &quot; + i + &quot;/&quot; + MAX_COUNT);
    }
    long endtime = System.currentTimeMillis();
    LOG.info(&quot;第&quot; + (times + 1) + &quot;次测试, 耗时&quot; + (endtime - starttime) + &quot;ms&quot;);
    alltime += endtime - starttime;
}
LOG.info(&quot;平均耗时: &quot; + (alltime / TEST_TIMES) + &quot;ms&quot;);

记录的log如下:

11:59:25,703 DBTestMulti test  *** 以100000为一次批量操作, 执行批处理语句  ***
11:59:25,703 BaseDBTest cleanDB 初始化数据库
11:59:58,984 DBTestMulti test 第1次测试, 耗时33000ms
11:59:58,984 BaseDBTest cleanDB 初始化数据库
12:01:21,687 DBTestMulti test 第2次测试, 耗时32594ms
12:01:21,687 BaseDBTest cleanDB 初始化数据库
12:02:44,640 DBTestMulti test 第3次测试, 耗时32562ms
12:02:44,640 DBTestMulti test 平均耗时: 32718ms
12:02:47,093 DBTestMulti test  *** 以50000为一次批量操作, 执行批处理语句  ***
12:02:47,093 BaseDBTest cleanDB 初始化数据库
12:03:19,734 DBTestMulti test 第1次测试, 耗时32625ms
12:03:19,734 BaseDBTest cleanDB 初始化数据库
12:04:43,406 DBTestMulti test 第2次测试, 耗时33328ms
12:04:43,406 BaseDBTest cleanDB 初始化数据库
12:06:06,687 DBTestMulti test 第3次测试, 耗时32609ms
12:06:06,687 DBTestMulti test 平均耗时: 32854ms
12:06:09,125 DBTestMulti test  *** 以10000为一次批量操作, 执行批处理语句  ***
12:06:09,125 BaseDBTest cleanDB 初始化数据库
12:06:40,921 DBTestMulti test 第1次测试, 耗时31781ms
12:06:40,921 BaseDBTest cleanDB 初始化数据库
12:08:03,125 DBTestMulti test 第2次测试, 耗时32032ms
12:08:03,125 BaseDBTest cleanDB 初始化数据库
12:09:26,859 DBTestMulti test 第3次测试, 耗时32781ms
12:09:26,859 DBTestMulti test 平均耗时: 32198ms
12:09:29,296 DBTestMulti test  *** 以5000为一次批量操作, 执行批处理语句  ***
12:09:29,296 BaseDBTest cleanDB 初始化数据库
12:10:00,406 DBTestMulti test 第1次测试, 耗时31094ms
12:10:00,406 BaseDBTest cleanDB 初始化数据库
12:11:23,031 DBTestMulti test 第2次测试, 耗时31953ms
12:11:23,031 BaseDBTest cleanDB 初始化数据库
12:12:46,265 DBTestMulti test 第3次测试, 耗时32187ms
12:12:46,265 DBTestMulti test 平均耗时: 31744ms
12:12:48,718 DBTestMulti test  *** 以1000为一次批量操作, 执行批处理语句  ***
12:12:48,718 BaseDBTest cleanDB 初始化数据库
12:13:20,468 DBTestMulti test 第1次测试, 耗时31750ms
12:13:20,468 BaseDBTest cleanDB 初始化数据库
12:14:43,703 DBTestMulti test 第2次测试, 耗时32625ms
12:14:43,703 BaseDBTest cleanDB 初始化数据库
12:16:06,203 DBTestMulti test 第3次测试, 耗时32125ms
12:16:06,203 DBTestMulti test 平均耗时: 32166ms
12:16:08,656 DBTestMulti test  *** 以500为一次批量操作, 执行批处理语句  ***
12:16:08,656 BaseDBTest cleanDB 初始化数据库
12:16:40,906 DBTestMulti test 第1次测试, 耗时32235ms
12:16:40,906 BaseDBTest cleanDB 初始化数据库
12:18:03,625 DBTestMulti test 第2次测试, 耗时32547ms
12:18:03,625 BaseDBTest cleanDB 初始化数据库
12:19:27,343 DBTestMulti test 第3次测试, 耗时33265ms
12:19:27,343 DBTestMulti test 平均耗时: 32682ms
12:19:30,406 DBTestMulti test  *** 以100为一次批量操作, 执行批处理语句  ***
12:19:30,406 BaseDBTest cleanDB 初始化数据库
12:20:53,875 DBTestMulti test 第1次测试, 耗时32782ms
12:20:53,875 BaseDBTest cleanDB 初始化数据库
12:22:17,765 DBTestMulti test 第2次测试, 耗时33687ms
12:22:17,765 BaseDBTest cleanDB 初始化数据库
12:23:41,609 DBTestMulti test 第3次测试, 耗时33531ms
12:23:41,609 DBTestMulti test 平均耗时: 33333ms
12:23:44,421 DBTestMulti test  *** 以50为一次批量操作, 执行批处理语句  ***
12:23:44,421 BaseDBTest cleanDB 初始化数据库
12:25:07,578 DBTestMulti test 第1次测试, 耗时32500ms
12:25:07,578 BaseDBTest cleanDB 初始化数据库
12:26:31,796 DBTestMulti test 第2次测试, 耗时33718ms
12:26:31,796 BaseDBTest cleanDB 初始化数据库
12:27:55,640 DBTestMulti test 第3次测试, 耗时33562ms
12:27:55,640 DBTestMulti test 平均耗时: 33260ms
12:27:58,468 BaseDBTest cleanDB 初始化数据库
14:08:25,484 DBTestSingle test 第1次测试, 耗时5976406ms
14:08:25,484 BaseDBTest cleanDB 初始化数据库
15:47:18,828 DBTestSingle test 第2次测试, 耗时5882735ms
15:47:18,828 BaseDBTest cleanDB 初始化数据库

其中批处理都是执行3次取平均值, 而逐行操作, 一次执行时间就要1个半小时, 所以只跑了两次. 日志筛选如下:

每次100000条, 分2次处理: 32718ms

每次50000条, 分4次处理: 32854ms

每次10000条, 分20次处理: 32198ms

每次5000条, 分40次处理: 31744ms

每次1000条, 分200次处理: 32166ms

每次500条, 分400次处理: 32682ms

每次100条, 分2000次处理: 33333ms

每次50条, 分4000次处理: 33260ms

而最后的两条是没有使用批处理跑出来的 用时近600000ms, 近1小时40分钟, 完全不是一个数量级的.

相比较而言, 批处理语句在量的划分上差别并不明显, 在千级的划分只有一点点的减少.

详细测试间测试代码(已上传).

点击下载: db-batch-test - 已下载 214 次

解决JDK1.6下SunJCE() is not accessible问题

一个老的项目从JDK1.5迁移到1.6时, Eclipse抛出一堆错误 主要是:

Access restriction: The constructor SunJCE() is not accessible due to restriction on required library /pp/develop/install/jdk1.6.0_21/jre/lib/ext/sunjce_provider.jar

访问限制: 由于/pp/develop/install/jdk1.6.0_21/jre/lib/ext/sunjce_provider.jar库的限制, SunJCE() 构造函数不可访问.

在网上搜了一下, 解决办法是修改编译选项, 方法如下: Window ->Preferences -> Java -> Compiler -> Errors/Warnings -> Deprecated and restricted API -> Forbidden reference (access rules) -> Warnings或者Ignore即可.

proxool 0.8.3使用java搭建MySql连接池的配置和测试

在企业开发的时候, 很多情况都会使用数据库连接, 下面提出使用配置方案.  

下载地址: http://proxool.sourceforge.net/  

创建一个xml配置文件db.xml, 放在CLASSPATH路径:  

<?xml version="1.0" encoding="UTF-8"?>
<something-else-entirely>
 <proxool>
  <!-- 数据源的别名 -->
  <alias>search</alias>
  <driver-url>jdbc:mysql://localhost:3306/search
  </driver-url>
  <driver-class>com.mysql.jdbc.Driver
  </driver-class>
  <!-- 连接池使用状况统计 -->
  <statistics>1m,15m,1d</statistics>
  <driver-properties>
   <property name="user" value="root" />
   <property name="password" value="root" />
   <property name="characterEncoding" value="UTF-8" />
   <property name="useUnicode" value="true" />
  </driver-properties> 

  <!-- proxool自动侦察各个连接状态的时间间隔(毫秒) 
    侦察到空闲的连接就马上回收,超时的销毁 默认30秒 -->
  <house-keeping-sleep-time>90000</house-keeping-sleep-time> 

  <!-- 连接池中可用的连接数量.如果当前的连接池中的连接少于这个数值. 
    新的连接将被建立 (假设没有超过最大可用数).例如.我们有3个活动连接2个可用连接 
    而我们的prototype-count是4,那么数据库连接池将试图建立另外2个连接.这和 
    minimum-connection-count 不同. minimum-connection-count把活动的连接 
    也计算在内.prototype-count  是spare connections 的数量. -->
  <prototype-count>5</prototype-count> 

  <!-- 最大连接数(默认5个),超过了这个连接数,再有请求时,就排在队列中等候, 最大的等待 
    请求数由maximum-new-connections决定 -->
  <maximum-connection-count>100</maximum-connection-count>
        <simultaneous-build-throttle>100</simultaneous-build-throttle>
  <!--最小连接数(默认2个) -->
  <minimum-connection-count>2</minimum-connection-count>
  <!-- 如果housekeeper 检测到某个线程的活动时间大于这个数值.它将会杀掉这线程 
    所以确认一下你的服务器的带宽.然后定一个合适的值.默认是5分钟 -->
  <maximum-active-time>300000</maximum-active-time>
  <house-keeping-test-sql>select CURRENT_DATE</house-keeping-test-sql>
 </proxool>
</something-else-entirely>

如果有多个数据库, 则要使用多个proxool标签, 配置不同的alias.  

然后配置web.xml, 里见加入:  

    <servlet> 
        <description>proxool配置servlet</description>
        <servlet-name>ServletConfigurator</servlet-name>
        <servlet-class>org.logicalcobwebs.proxool.configuration.ServletConfigurator</servlet-class>
        <init-param>
            <param-name>xmlFile</param-name> 
            <!-- 这里是上面那个配置文件的路径 -->
            <param-value>WEB-INF/classes/db.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet> 
        <description>proxool管理servlet</description>
        <servlet-name>proxool</servlet-name>
        <servlet-class>org.logicalcobwebs.proxool.admin.servlet.AdminServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>proxool</servlet-name>
        <url-pattern>/admin</url-pattern>
    </servlet-mapping>

创建一个DBMgr.java, 作为连接数据库的模板类  

package com.pptv.config;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException; 

import org.apache.log4j.Logger; 

/**
 * 数据库的基本连接类, 通过proxool连接池连接Mysql
 *
 * @author Dan Shan
 *
 */
public final class DBMgr {
    /**
     * Log4j.
     */
    private static final Logger LOG = Logger.getLogger(DBMgr.class);
    static {
        try {
            Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
        } catch (ClassNotFoundException e) {
            LOG.error("Load proxool failed: " + e);
        }
    } 

    /**
     * 所有方法都为static, 不允许实例化.
     */
    private DBMgr() {
    } 

    public static Connection getDBconn(final String dbName)
            throws SQLException {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection("proxool." + dbName);
        } catch (Exception e) {
            LOG.error("Read the database configuration files error: " + e);
        }
        return conn;
    }
}

至于怎么用, 都应该知道了, 调用getDBconn时, 传入最上面db.xml中设定的alias值, 即可连接不同的数据库.  

再来说测试, 使用单元测试的时候, 并不希望每次都启动网站来测试, 希望直接就能调用数据库的配置. 其实也很简单:  

package com.pptv.config; 
import java.sql.Connection;
import java.sql.SQLException; 

import junit.framework.TestCase; 

import org.junit.Test;
import org.logicalcobwebs.proxool.ProxoolException;
import org.logicalcobwebs.proxool.ProxoolFacade;
import org.logicalcobwebs.proxool.configuration.JAXPConfigurator; 

public class DBMgrTest
        extends TestCase { 

    private Connection conn; 

    @Override
    protected void setUp()
            throws Exception {
        super.setUp();
        String dbConfigFile = DBMgrTest.class.getResource("/db.xml").getPath();
        try {
            JAXPConfigurator.configure(dbConfigFile, false);
        } catch (ProxoolException e) {
            e.printStackTrace();
        }
    } 

    @Override
    protected void tearDown()
            throws Exception {
        super.tearDown();
        if (conn != null) {
            conn.close();
        }
        ProxoolFacade.shutdown(0);
    } 

    @Test
    public void testGetDBconn()
            throws SQLException {
        conn = DBMgr.getDBconn("search");
        assertNotNull(conn);
    }
}

只要手动加载配置文件, 见setUp()方法.  

这里注意一下, 单元测试需要手动的关闭连接池, 否则测试程序停止后会抛异常, 见tearDown()方法的最后一行:  

ProxoolFacade.shutdown(0);

连接池个别情况不能替代传统的jdbc连接, 比如需要建立长连接时, 这个时候, 可能就需要创建一个保持连接的Connection, 而不能使用proxool了, 因为会被自动Kill. 

点击下载: proxool-config - 已下载 193 次