Posts Tagged ‘ mysql

解决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 次

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 次