基于lucene的案例开发:数据库连接池

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43272993

通过java程序去连接数据库时,使用的协议是TCP/IP协议,TCP/IP协议需要进行3次握手。如果每一次数据库操作都需要创建一个新的连接,都要进行3次握手,这是十分浪费资源的,程序的效率也不是很高。为了解决这个问题,我们想可不可以自己维护一些数据库连接,需要数据库操作的时候,直接使用这其中的一个连接,用完了,在还给它,这样的话就不需要每次数据库操作都创建一个新的连接了。这种思维模式就是今天的博客主题数据库连接池。

基本原理

连接池技术的核心思想是:连接复用,通过建立一个数据库连接池以及一套连接使用、分配、管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。另外,由于对JDBC中的原始连接进行了封装,从而方便了数据库应用对于连接的使用(特别是对于事务处理),提高了开发效率,也正是因为这个封装层的存在,隔离了应用的本身的处理逻辑和具体数据库访问逻辑,使应用本身的复用成为可能。连接池主要由三部分组成(如下图所示):连接池的建立、连接池中连接的使用管理、连接池的关闭。

连接池实现

这里的介绍的连接池是proxool,这里对其做了进一步的封装,使数据库操作更加简单。

DBPool

DBPool类用于指定数据库连接池的配置文件,源程序如下:

 /**
 *@Description:  数据库连接池配置
 */
package com.lulei.db.manager;  

import org.apache.log4j.Logger;

import com.lulei.util.ClassUtil;

public class DBPool {
	private static DBPool dbPool = null;
	private String poolPath;
	private static Logger log = Logger.getLogger(DBPool.class);
	private static String path = ClassUtil.getClassRootPath(DBPool.class);

	public static DBPool getDBPool(){
		if (dbPool == null){
			synchronized(DBPool.class){
				if (dbPool == null){
					dbPool = new DBPool();
				}
			}
		}
		return dbPool;
	}

	private DBPool(){

	}

	/**
	 * @param poolPath
	 * @Author: lulei
	 * @Description: 设置数据库连接池配置文件路径
	 */
	public void setPoolPath(String poolPath){
		this.poolPath = poolPath;
	}

	/**
	 * @return
	 * @Author: lulei
	 * @Description: 返回数据库连接池配置文件路径
	 */
	protected String getPoolPath(){
		//如果没有指定配置文件,则使用默认配置文件
		if (poolPath == null){
			poolPath = path + "proxool.xml";
			log.info("Database's poolpath is null, use default path:" + poolPath);
		}
		return poolPath;
	}
}

在配置文件中,参数介绍如下:

fatal-sql-exception: 它是一个逗号分割的信息片段.当一个SQL异常发生时,他的异常信息将与这个信息片段进行比较.如果在片段中存在,那么这个异常将被认为是个致命错误(Fatal SQL Exception ).这种情况下,数据库连接将要被放弃.无论发生什么,这个异常将会被重掷以提供给消费者.用户最好自己配置一个不同的异常来抛出.

fatal-sql-exception-wrapper-class:正如上面所说,你最好配置一个不同的异常来重掷.利用这个属性,用户可以包装SQLException,使他变成另外一个异常.这个异常或者继承SQLException或者继承字RuntimeException.proxool自带了2个实现:‘org.logicalcobwebs.proxool.FatalSQLException‘ 和‘org.logicalcobwebs.proxool.FatalRuntimeException‘ .后者更合适.

house-keeping-sleep-time: house keeper 保留线程处于睡眠状态的最长时间,house keeper 的职责就是检查各个连接的状态,并判断是否需要销毁或者创建.

house-keeping-test-sql:  如果发现了空闲的数据库连接.house keeper 将会用这个语句来测试.这个语句最好非常快的被执行.如果没有定义,测试过程将会被忽略。

injectable-connection-interface: 允许proxool实现被代理的connection对象的方法.

injectable-statement-interface: 允许proxool实现被代理的Statement 对象方法.

injectable-prepared-statement-interface: 允许proxool实现被代理的PreparedStatement 对象方法.

injectable-callable-statement-interface: 允许proxool实现被代理的CallableStatement 对象方法.

jmx: 略

jmx-agent-id: 略

jndi-name: 数据源的名称

maximum-active-time: 如果housekeeper 检测到某个线程的活动时间大于这个数值.它将会杀掉这个线程.所以确认一下你的服务器的带宽.然后定一个合适的值.默认是5分钟.

maximum-connection-count: 最大的数据库连接数.

maximum-connection-lifetime: 一个线程的最大寿命.

minimum-connection-count: 最小的数据库连接数

overload-without-refusal-lifetime: 略

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

recently-started-threshold:  略

simultaneous-build-throttle:  略

statistics:  连接池使用状况统计。 参数“10s,1m,1d”

statistics-log-level:  日志统计跟踪类型。 参数“ERROR”或 “INFO”

test-before-use: 略

test-after-use: 略

trace: 如果为true,那么每个被执行的SQL语句将会在执行期被log记录(DEBUG LEVEL).你也可以注册一个ConnectionListener (参看ProxoolFacade)得到这些信息.

verbose: 详细信息设置。 参数 bool 值

在本例中数据库连接池配置文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<proxool-config>
<proxool>
    <alias>novelSelect</alias>
   	<driver-url><![CDATA[jdbc:mysql://172.20.37.73:3306/novel?characterEncoding=utf-8]]></driver-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <driver-properties>
      <property name="user" value="root"/>
      <property name="password" value="root"/>
    </driver-properties>
     <house-keeping-sleep-time>900000</house-keeping-sleep-time>
	 <maximum-active-time>500000</maximum-active-time>
	 <maximum-connection-count>40</maximum-connection-count>
	 <minimum-connection-count>4</minimum-connection-count>
    <house-keeping-test-sql>select 1</house-keeping-test-sql>
    <prop key="hibernate.connection.release_mode">after_transaction</prop>
</proxool>
<proxool>
    <alias>novelEdit</alias>
   	<driver-url><![CDATA[jdbc:mysql://172.20.37.73:3306/novel?characterEncoding=utf-8]]></driver-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <driver-properties>
      <property name="user" value="root"/>
      <property name="password" value="root"/>
    </driver-properties>
     <house-keeping-sleep-time>900000</house-keeping-sleep-time>
	 <maximum-active-time>500000</maximum-active-time>
	 <maximum-connection-count>10</maximum-connection-count>
	 <minimum-connection-count>4</minimum-connection-count>
    <house-keeping-test-sql>select 1</house-keeping-test-sql>
    <prop key="hibernate.connection.release_mode">after_transaction</prop>
</proxool>
</proxool-config>

DBManager

DBManager在系统中是单例模式,在初始化只需要简单的两句代码:

			JAXPConfigurator.configure(DBPool.getDBPool().getPoolPath(), false);
			Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");

获取数据库连接也只是简单的一句代码:

return DriverManager.getConnection(poolName);

DBManager源代码如下:

 /**
 *@Description: 数据库连接池管理
 */
package com.lulei.db.manager;  

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.logicalcobwebs.proxool.configuration.JAXPConfigurator;

public class DBManager {

	private static DBManager dBManager = null;

	private DBManager(){
		try {
			JAXPConfigurator.configure(DBPool.getDBPool().getPoolPath(), false);
			Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
		} catch (Exception  e){
			e.printStackTrace();
		}
	}

	/**
	 * @return DBManager
	 * @Author: lulei
	 * @Description: 获取数据库连接池管理对象
	 */
	protected static DBManager getDBManager(){
		if (dBManager == null){
			synchronized(DBManager.class){
				if (dBManager == null){
					dBManager = new DBManager();
				}
			}
		}
		return dBManager;
	}

	/**
	 * @param poolName
	 * @return Connection
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: 获取数据库链接
	 */
	protected Connection getConnection(String poolName) throws SQLException{
		return DriverManager.getConnection(poolName);
	}
}

DBOperation

为了简化数据库的操作,对数据库操作进行再一次封装成DBOperation类。在setPres方法中,这里只做了几种简单的数据类型,关于其他复杂的数据类型可以根据项目需要添加。源代码如下:

 /**
 *@Description: 数据库操作
 */
package com.lulei.db.manager;  

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;

import org.apache.log4j.Logger;

public class DBOperation {

	private static Logger log = Logger.getLogger(DBOperation.class);
	private Connection conn = null;
	private String poolName;

	/**
	* @param poolName
	 */
	public DBOperation(String poolName){
		this.poolName = poolName;
	}

	/**
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: 获取Connection
	 */
	private void open() throws SQLException{
		this.conn = DBManager.getDBManager().getConnection(poolName);
	}

	/**
	 * @Author: lulei
	 * @Description: 关闭Connection
	 */
	public void close() {
		try {
			if (this.conn != null) {
				this.conn.close();
			}
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @param sql组装的sql字符串
	 * @param params传入的参数
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @Author: lulei
	 * @Description: 组装PreparedStatement
	 */
	private PreparedStatement setPres(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{
		if (null != params) {
			if (0 < params.size()){
				PreparedStatement pres = this.conn.prepareStatement(sql);
				for (int i = 1; i <= params.size(); i++){
					if (params.get(i).getClass() == Class.forName("java.lang.String")){
						pres.setString(i, params.get(i).toString());
					} else if (params.get(i).getClass() == Class.forName("java.lang.Integer")){
						pres.setInt(i, (Integer) params.get(i));
					} else if (params.get(i).getClass() == Class.forName("java.lang.Boolean")){
						pres.setBoolean(i, (Boolean) params.get(i));
					} else if (params.get(i).getClass() == Class.forName("java.lang.Float")){
						pres.setFloat(i, (Float) params.get(i));
					} else if (params.get(i).getClass() == Class.forName("java.lang.Double")){
						pres.setDouble(i, (Double) params.get(i));
					} else if (params.get(i).getClass() == Class.forName("java.lang.Long")){
						pres.setLong(i, (Long) params.get(i));
					} else if (params.get(i).getClass() == Class.forName("java.sql.Date")){
						pres.setDate(i, java.sql.Date.valueOf(params.get(i).toString()));
					} else {
						log.info("not found class : " + params.get(i).getClass().toString());
						return null;
					}
				}
				return pres;
			}
		}
		return null;
	}

	/**
	 * @param sql
	 * @return int
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: executeUpdate
	 */
	protected int executeUpdate(String sql) throws SQLException{
		this.open();
		Statement state = this.conn.createStatement();
		int re = state.executeUpdate(sql);
		return re;
	}

	/**
	 * executeUpdate
	 * @param sql
	 * @param params
	 * @return int
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @Author: lulei
	 * @Description:
	 */
	protected int executeUpdate(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{
		this.open();
		PreparedStatement pres = setPres(sql, params);
		int re = 0;
		if (null != pres) {
			re = pres.executeUpdate();
		}
		return re;
	}

	/**
	 * getGeneratedKeys
	 * @param sql
	 * @return ResultSet
	 * @throws SQLException
	 * @Author: lulei
	 * @Description:
	 */
	protected ResultSet getGeneratedKeys(String sql) throws SQLException{
		this.open();
		Statement state = this.conn.createStatement();
		state.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
		ResultSet re = state.getGeneratedKeys();
		return re;
	}

	/**
	 * getGeneratedKeys
	 * @param sql
	 * @param params
	 * @return ResultSet
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @Author: lulei
	 * @Description:
	 */
	protected ResultSet getGeneratedKeys(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{
		this.open();
		PreparedStatement pres = setPres(sql, params);
		if (null != pres) {
			pres.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
			ResultSet re = pres.getGeneratedKeys();
			return re;
		}
		return null;
	}

	/**
	 * @param sql
	 * @return ResultSet
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: executeQuery
	 */
	protected ResultSet executeQuery(String sql) throws SQLException{
		this.open();
		Statement state = this.conn.createStatement();
		ResultSet re = state.executeQuery(sql);
		return re;
	}

	/**
	 * @param sql
	 * @param params
	 * @return ResultSet
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @Author: lulei
	 * @Description: executeQuery
	 */
	protected ResultSet executeQuery(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{
		this.open();
		PreparedStatement pres = setPres(sql, params);
		if (null != pres) {
			ResultSet re = pres.executeQuery();
			return re;
		}
		return null;
	}
}

DBServer

DBServer对数据库的增删该查操作进行进一步的细化,源代码如下:

 /**
 *@Description:  增删改查四个数据库操作接口
 */
package com.lulei.db.manager;  

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;

public class DBServer {

	private DBOperation dBOperation;

	/**
	* @param poolName
	* @Description: 在使用该类之前,请保证函数DBPool.getDBPool().setPoolPath()已经运行
	 */
	public DBServer(String poolName){
		dBOperation = new DBOperation(poolName);
	}

	/**
	 * @Author: lulei
	 * @Description: 释放链接,在执行完数据库操作,必须执行此命令
	 */
	public void close(){
		dBOperation.close();
	}

	/**
	 * @param table
	 * @param columns
	 * @param params
	 * @return int
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @Author: lulei
	 * @Description: insert 执行完此命令后,执行close()操作
	 */
	public int insert(String table, String columns, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{
		String sql = insertSql(columns, table);
		return dBOperation.executeUpdate(sql, params);
	}

	/**
	 * @param sql
	 * @return int
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: insert 执行完此命令后,执行close()操作
	 */
	public int insert(String sql) throws SQLException {
		return dBOperation.executeUpdate(sql);
	}

	/**
	 * @param table
	 * @param columns
	 * @param params
	 * @return ResultSet
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @Author: lulei
	 * @Description: insertGetGeneratedKeys 执行完此命令后,执行close()操作
	 */
	public ResultSet insertGetGeneratedKeys(String table, String columns, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{
		String sql = insertSql(columns, table);
		return dBOperation.getGeneratedKeys(sql, params);
	}

	/**
	 * @param sql
	 * @return ResultSet
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: insertGetGeneratedKeys 执行完此命令后,执行close()操作
	 */
	public ResultSet insertGetGeneratedKeys(String sql) throws SQLException{
		return dBOperation.getGeneratedKeys(sql);
	}

	/**
	 * @param table
	 * @param condition
	 * @return int
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: delete 执行完此命令后,执行close()操作
	 */
	public int delete(String table, String condition) throws SQLException{
		if(null == table){
			return 0;
		}
		String sql = "delete from " + table + " " + condition;
		return dBOperation.executeUpdate(sql);
	}

	/**
	 * @param sql
	 * @return int
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: delete 执行完此命令后,执行close()操作
	 */
	public int delete(String sql) throws SQLException{
		return dBOperation.executeUpdate(sql);
	}

	/**
	 * @param columns
	 * @param table
	 * @param condition
	 * @return ResultSet
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: select 执行完此命令后,执行close()操作
	 */
	public ResultSet select(String columns, String table, String condition) throws SQLException {
		String sql = "select " + columns + " from " + table + " " + condition;
		return dBOperation.executeQuery(sql);
	}

	/**
	 * @param sql
	 * @return ResultSet
	 * @throws SQLException
	 * @Author: lulei
	 * @Description: select 执行完此命令后,执行close()操作
	 */
	public ResultSet select(String sql) throws SQLException{
		return dBOperation.executeQuery(sql);
	}

	/**
	 * @param table
	 * @param columns
	 * @param condition
	 * @param params
	 * @return int
	 * @throws SQLException
	 * @throws ClassNotFoundException
	 * @Author: lulei
	 * @Description: update 执行完此命令后,执行close()操作
	 */
	public int update(String table, String columns, String condition, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{
		String sql = updateString(table, columns, condition);
		return dBOperation.executeUpdate(sql, params);
	}

	/**
	 * @param sql
	 * @return int
	 * @throws SQLException
	 * @Author: lulei
	 * @Description:  update 执行完此命令后,执行close()操作
	 */
	public int update(String sql) throws SQLException{
		return dBOperation.executeUpdate(sql);
	}

	/**
	 * @param table
	 * @param columns
	 * @param condition
	 * @return String
	 * @Author: lulei
	 * @Description: 组装updateString
	 */
	private String updateString(String table, String columns, String condition) {
		if (null == columns || null == table) {
			return "";
		}
		String[] column = columns.split(",");
		StringBuilder stringBuilder = new StringBuilder("update ");
		stringBuilder.append(table);
		stringBuilder.append(" set ");
		stringBuilder.append(column[0]);
		stringBuilder.append("=?");
		for (int i = 1; i < column.length; i++){
			stringBuilder.append(", ");
			stringBuilder.append(column[i]);
			stringBuilder.append("=?");
		}
		stringBuilder.append(" ");
		stringBuilder.append(condition);
		return stringBuilder.toString();
	}

	/**
	 * @param columns
	 * @param table
	 * @return String
	 * @Author: lulei
	 * @Description: 组装insertSql
	 */
	private String insertSql(String columns, String table){
		if (null == columns || null == table) {
			return "";
		}
		int colNum = columns.split(",").length;
		StringBuilder stringBuilder = new StringBuilder("insert into ");
		stringBuilder.append(table);
		stringBuilder.append(" (");
		stringBuilder.append(columns);
		stringBuilder.append(") values (?");
		for (int i = 1; i < colNum; i++) {
			stringBuilder.append(",?");
		}
		stringBuilder.append(")");
		return stringBuilder.toString();
	}
}

下面是的使用事例是其他项目中的一个例子,这里可以简单的看下,代码如下:

	public boolean support(String docNo){
		DBServer dbServer = new DBServer(dbPoolName);
		String editTime = System.currentTimeMillis() + "";
		String sql = "update " + SolutionTable.tableName + " set " + SolutionTable.support + "=" + SolutionTable.support + "+1, " + SolutionTable.editTime + "='"+ editTime+"' where " +SolutionTable.docNo + "='" + docNo + "'";
		try {
			return dbServer.update(sql) > 0;
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			dbServer.close();
		}
		return false;
	}

ps:最近发现其他网站可能会对博客转载,上面并没有源链接,如想查看更多关于 基于lucene的案例开发点击这里。或访问网址http://blog.csdn.net/xiaojimanman/article/category/2841877

时间: 2024-10-10 07:44:59

基于lucene的案例开发:数据库连接池的相关文章

基于lucene的案例开发:ParseUtil &amp; ParseRequest

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43195045 这篇博客主要介绍ParseUtil类和ParseRequest类,因为这两个类都比较简单,所以这里就不会给出事例程序. ParseUtil ParseUtil类主要实现将字符串(数字)转化为数值,这个在读取配置文件或数据转化过程中有很大的作用.源程序如下: /** *@Description: 转换类 */ package com.lulei.util; publ

基于lucene的案例开发:ClassUtil &amp; CharsetUtil

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43194793 这篇博客主要介绍ClassUtil类和CharsetUtil类.这两个也是项目中比较常用的类,一个用于指定文件路径,一个用于检测文件的编码方式. ClassUtil ClassUtil类中的方法主要是返回class文件所在的文件目录或工程的根目录地址,这主要用于指定工程中配置文件的路径,不至于环境迁移而导致配置文件路径错误.源代码如下: /** * @Descri

基于lucene的案例开发:实时索引的检索

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44279753 http://www.llwjy.com/blogdetail/31bb705106379feaf6d31b58dd777be6.html 个人博客小站搭建成功,网址 www.llwjy.com,欢迎大家来吐槽~ 在前面的博客中,我们已经介绍了IndexSearcher中的检索方法,也介绍了如何基于lucene中的NRT*类去创建实时索引,在这篇博客中我们就重点介

基于lucene的案例开发:实时索引的修改

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44280311 http://www.llwjy.com/blogdetail/e42fa5c3097f4964fca0fdfe7cd7a9a2.html 个人的博客小站已经上线了,网址 www.llwjy.com,欢迎大家来吐槽~ 上一篇博客已经介绍了实时索引的检索功能,这个就相当于数据的的查询功能,我们都知道数据库的增删改查是最常用的四个功能,实时索引也是一样,他也有增删改查

基于lucene的案例开发:纵横小说分布式采集

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/46812645 http://www.llwjy.com/blogdetail/9df464b20cca5405c7ce07e2fb2d768f.html 个人博客站已经上线了,网址 www.llwjy.com ~欢迎各位吐槽~ ----------------------------------------------------------------------------

基于lucene的案例开发:案例初识

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43192055 首先抱歉,这几天在准备案例的整体框架设计,所以更新就断了几天,还请原谅. 案例整体介绍 在我们开始正式的案例开发介绍之前,我们先看一下整体的案例demo介绍,明白案例是做什么的. 从上图中,我们可以看出,这个案例主要是通过爬虫程序去采集纵横小说上的资源,然后将资源存储到自己的数据库中,将数据库中的需要检索的数据通过lucene建立索引文件,最后通过web服务展示数

基于lucene的案例开发:查询语句创建PackQuery

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44656141 http://www.llwjy.com/blogdetail/162e5e70516d7ddfb6df8f77e6b13a2b.html 个人博客站已经上线了,网址 www.llwjy.com~欢迎各位吐槽 ------------------------------------------------------------------------------

基于lucene的案例开发:纵横小说阅读页采集

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44937073 http://www.llwjy.com/blogdetail/29bd8de30e8d17871c707b76ec3212b0.html 个人博客站已经上线了,网址 www.llwjy.com ~欢迎各位吐槽~ ----------------------------------------------------------------------------

基于lucene的案例开发:纵横小说简介页采集

转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/44851419 http://www.llwjy.com/blogdetail/1b5ae17c513d127838c2e02102b5bb87.html 个人博客站已经上线了,网址 www.llwjy.com ~欢迎各位吐槽~ ----------------------------------------------------------------------------