Java jdbc数据库连接池总结
来源:互联网 发布:足球赛程编排软件 编辑:程序博客网 时间:2024/05/22 12:41
转自:http://blog.csdn.net/wangtao7752/article/details/8350743
1. 引言
近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机应用程序已从传统的桌面应用转到Web应用。基于B/S(Browser/Server)架构的3层开发模式逐渐取代C/S(Client/Server)架构的开发模式,成为开发企业级应用和电子商务普遍采用的技术。在Web应用开发的早期,主要使用的技术是CGI﹑ASP﹑PHP等。之后,Sun公司推出了基于Java语言的Servlet+Jsp+JavaBean技术。相比传统的开发技术,它具有跨平台﹑安全﹑有效﹑可移植等特性,这使其更便于使用和开发。
Java应用程序访问数据库的基本原理
在Java语言中,JDBC(Java DataBase Connection)是应用程序与数据库沟通的桥梁,即Java语言通过JDBC技术访问数据库。JDBC是一种“开放”的方案,它为数据库应用开发人员﹑数据库前台工具开发人员提供了一种标准的应用程序设计接口,使开发人员可以用纯Java语言编写完整的数据库应用程序。JDBC提供两种API,分别是面向开发人员的API和面向底层的JDBC驱动程序API,底层主要通过直接的JDBC驱动和JDBC-ODBC桥驱动实现与数据库的连接。
一般来说,Java应用程序访问数据库的过程(如图1所示)是:
①装载数据库驱动程序;
②通过JDBC建立数据库连接;
③访问数据库,执行SQL语句;
④断开数据库连接。
图1 Java数据库访问机制
JDBC作为一种数据库访问技术,具有简单易用的优点。但使用这种模式进行Web应用程序开发,存在很多问题:首先,每一次Web请求都要建立一次数据库连接。建立连接是一个费时的活动,每次都得花费0.05s~1s的时间,而且系统还要分配内存资源。这个时间对于一次或几次数据库操作,或许感觉不出系统有多大的开销。可是对于现在的Web应用,尤其是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种情况下,频繁的进行数据库连接操作势必占用很多的系统资源,网站的响应速度必定下降,严重的甚至会造成服务器的崩溃。不是危言耸听,这就是制约某些电子商务网站发展的技术瓶颈问题。其次,对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将不得不重启数据库。还有,这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
由上面的分析可以看出,问题的根源就在于对数据库连接资源的低效管理。我们知道, 对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。连接池的基本工作原理见下图2。
图2 连接池的基本工作原理
2、服务器自带的连接池
连接池的实现
2、连接池实现(经过本人改版,可以适用多数据库类型的应用以及一种数据库类型多个数据库且数据 库的数量可以动态增加的应用程序)
1),DBConnectionPool.java 数据库连接池类
2),DBConnectionManager .java 数据库管理类
3),DSConfigBean .java 单个数据库连接信息Bean
4),ParseDSConfig.java 操作多(这个'多'包括不同的数据库和同一种数据库有多个数据库)
数据 配置文件xml
5),ds.config.xml 数据库配置文件xml
原代码如下:
- DBConnectionPool.java
- ----------------------------------------------------------
- /**
- * 数据库连接池类
- */
- import java.sql.Connection;
- import java.sql.DriverManager;
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.Iterator;
- import java.util.Timer;
- public class DBConnectionPool implements TimerListener {
- private Connection con=null;
- private int inUsed=0; //使用的连接数
- private ArrayList freeConnections = new ArrayList();//容器,空闲连接
- private int minConn; //最小连接数
- private int maxConn; //最大连接
- private String name; //连接池名字
- private String password; //密码
- private String url; //数据库连接地址
- private String driver; //驱动
- private String user; //用户名
- public Timer timer; //定时
- /**
- *
- */
- public DBConnectionPool() {
- // TODO Auto-generated constructor stub
- }
- /**
- * 创建连接池
- * @param driver
- * @param name
- * @param URL
- * @param user
- * @param password
- * @param maxConn
- */
- public DBConnectionPool(String name, String driver,String URL, String user, String password, int maxConn)
- {
- this.name=name;
- this.driver=driver;
- this.url=URL;
- this.user=user;
- this.password=password;
- this.maxConn=maxConn;
- }
- /**
- * 用完,释放连接
- * @param con
- */
- public synchronized void freeConnection(Connection con)
- {
- this.freeConnections.add(con);//添加到空闲连接的末尾
- this.inUsed--;
- }
- /**
- * timeout 根据timeout得到连接
- * @param timeout
- * @return
- */
- public synchronized Connection getConnection(long timeout)
- {
- Connection con=null;
- if(this.freeConnections.size()>0)
- {
- con=(Connection)this.freeConnections.get(0);
- if(con==null)con=getConnection(timeout); //继续获得连接
- }
- else
- {
- con=newConnection(); //新建连接
- }
- if(this.maxConn==0||this.maxConn<this.inUsed)
- {
- con=null;//达到最大连接数,暂时不能获得连接了。
- }
- if(con!=null)
- {
- this.inUsed++;
- }
- return con;
- }
- /**
- *
- * 从连接池里得到连接
- * @return
- */
- public synchronized Connection getConnection()
- {
- Connection con=null;
- if(this.freeConnections.size()>0)
- {
- con=(Connection)this.freeConnections.get(0);
- this.freeConnections.remove(0);//如果连接分配出去了,就从空闲连接里删除
- if(con==null)con=getConnection(); //继续获得连接
- }
- else
- {
- con=newConnection(); //新建连接
- }
- if(this.maxConn==0||this.maxConn<this.inUsed)
- {
- con=null;//等待 超过最大连接时
- }
- if(con!=null)
- {
- this.inUsed++;
- System.out.println("得到 "+this.name+" 的连接,现有"+inUsed+"个连接在使用!");
- }
- return con;
- }
- /**
- *释放全部连接
- *
- */
- public synchronized void release()
- {
- Iterator allConns=this.freeConnections.iterator();
- while(allConns.hasNext())
- {
- Connection con=(Connection)allConns.next();
- try
- {
- con.close();
- }
- catch(SQLException e)
- {
- e.printStackTrace();
- }
- }
- this.freeConnections.clear();
- }
- /**
- * 创建新连接
- * @return
- */
- private Connection newConnection()
- {
- try {
- Class.forName(driver);
- con=DriverManager.getConnection(url, user, password);
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- System.out.println("sorry can't find db driver!");
- } catch (SQLException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- System.out.println("sorry can't create Connection!");
- }
- return con;
- }
- /**
- * 定时处理函数
- */
- public synchronized void TimerEvent()
- {
- //暂时还没有实现以后会加上的
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- }
- /**
- * @return the driver
- */
- public String getDriver() {
- return driver;
- }
- /**
- * @param driver the driver to set
- */
- public void setDriver(String driver) {
- this.driver = driver;
- }
- /**
- * @return the maxConn
- */
- public int getMaxConn() {
- return maxConn;
- }
- /**
- * @param maxConn the maxConn to set
- */
- public void setMaxConn(int maxConn) {
- this.maxConn = maxConn;
- }
- /**
- * @return the minConn
- */
- public int getMinConn() {
- return minConn;
- }
- /**
- * @param minConn the minConn to set
- */
- public void setMinConn(int minConn) {
- this.minConn = minConn;
- }
- /**
- * @return the name
- */
- public String getName() {
- return name;
- }
- /**
- * @param name the name to set
- */
- public void setName(String name) {
- this.name = name;
- }
- /**
- * @return the password
- */
- public String getPassword() {
- return password;
- }
- /**
- * @param password the password to set
- */
- public void setPassword(String password) {
- this.password = password;
- }
- /**
- * @return the url
- */
- public String getUrl() {
- return url;
- }
- /**
- * @param url the url to set
- */
- public void setUrl(String url) {
- this.url = url;
- }
- /**
- * @return the user
- */
- public String getUser() {
- return user;
- }
- /**
- * @param user the user to set
- */
- public void setUser(String user) {
- this.user = user;
- }
- }
- -------------------------------------------
- DBConnectionManager .java
- ------------------------------------------
- /**
- * 数据库连接池管理类
- */
- import java.sql.Connection;
- import java.util.ArrayList;
- import java.util.Enumeration;
- import java.util.HashMap;
- import java.util.Hashtable;
- import java.util.Iterator;
- import java.util.Properties;
- import java.util.Vector;
- import com.chunkyo.db.ParseDSConfig;
- import com.chunkyo.db.DSConfigBean;
- import com.chunkyo.db.DBConnectionPool;
- public class DBConnectionManager {
- static private DBConnectionManager instance;//唯一数据库连接池管理实例类
- static private int clients; //客户连接数
- private Vector drivers = new Vector();//驱动信息
- private Hashtable pools=new Hashtable();//连接池
- /**
- * 实例化管理类
- */
- public DBConnectionManager() {
- // TODO Auto-generated constructor stub
- this.init();
- }
- /**
- * 得到唯一实例管理类
- * @return
- */
- static synchronized public DBConnectionManager getInstance()
- {
- if(instance==null)
- {
- instance=new DBConnectionManager();
- }
- return instance;
- }
- /**
- * 释放连接
- * @param name
- * @param con
- */
- public void freeConnection(String name, Connection con)
- {
- DBConnectionPool pool=(DBConnectionPool)pools.get(name);//根据关键名字得到连接池
- if(pool!=null)
- pool.freeConnection(con);//释放连接
- }
- /**
- * 得到一个连接根据连接池的名字name
- * @param name
- * @return
- */
- public Connection getConnection(String name)
- {
- DBConnectionPool pool=null;
- Connection con=null;
- pool=(DBConnectionPool)pools.get(name);//从名字中获取连接池
- con=pool.getConnection();//从选定的连接池中获得连接
- if(con!=null)
- System.out.println("得到连接。。。");
- return con;
- }
- /**
- * 得到一个连接,根据连接池的名字和等待时间
- * @param name
- * @param time
- * @return
- */
- public Connection getConnection(String name, long timeout)
- {
- DBConnectionPool pool=null;
- Connection con=null;
- pool=(DBConnectionPool)pools.get(name);//从名字中获取连接池
- con=pool.getConnection(timeout);//从选定的连接池中获得连接
- System.out.println("得到连接。。。");
- return con;
- }
- /**
- * 释放所有连接
- */
- public synchronized void release()
- {
- Enumeration allpools=pools.elements();
- while(allpools.hasMoreElements())
- {
- DBConnectionPool pool=(DBConnectionPool)allpools.nextElement();
- if(pool!=null)pool.release();
- }
- pools.clear();
- }
- /**
- * 创建连接池
- * @param props
- */
- private void createPools(DSConfigBean dsb)
- {
- DBConnectionPool dbpool=new DBConnectionPool();
- dbpool.setName(dsb.getName());
- dbpool.setDriver(dsb.getDriver());
- dbpool.setUrl(dsb.getUrl());
- dbpool.setUser(dsb.getUsername());
- dbpool.setPassword(dsb.getPassword());
- dbpool.setMaxConn(dsb.getMaxconn());
- System.out.println("ioio:"+dsb.getMaxconn());
- pools.put(dsb.getName(), dbpool);
- }
- /**
- * 初始化连接池的参数
- */
- private void init()
- {
- //加载驱动程序
- this.loadDrivers();
- //创建连接池
- Iterator alldriver=drivers.iterator();
- while(alldriver.hasNext())
- {
- this.createPools((DSConfigBean)alldriver.next());
- System.out.println("创建连接池。。。");
- }
- System.out.println("创建连接池完毕。。。");
- }
- /**
- * 加载驱动程序
- * @param props
- */
- private void loadDrivers()
- {
- ParseDSConfig pd=new ParseDSConfig();
- //读取数据库配置文件
- drivers=pd.readConfigInfo("ds.config.xml");
- System.out.println("加载驱动程序。。。");
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- }
- }
- ----------------------------------------
- DSConfigBean.java
- ----------------------------------------
- /**
- * 配置文件Bean类
- */
- public class DSConfigBean {
- private String type =""; //数据库类型
- private String name =""; //连接池名字
- private String driver =""; //数据库驱动
- private String url =""; //数据库url
- private String username =""; //用户名
- private String password =""; //密码
- private int maxconn =0; //最大连接数
- /**
- *
- */
- public DSConfigBean() {
- // TODO Auto-generated constructor stub
- }
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- }
- /**
- * @return the driver
- */
- public String getDriver() {
- return driver;
- }
- /**
- * @param driver the driver to set
- */
- public void setDriver(String driver) {
- this.driver = driver;
- }
- /**
- * @return the maxconn
- */
- public int getMaxconn() {
- return maxconn;
- }
- /**
- * @param maxconn the maxconn to set
- */
- public void setMaxconn(int maxconn) {
- this.maxconn = maxconn;
- }
- /**
- * @return the name
- */
- public String getName() {
- return name;
- }
- /**
- * @param name the name to set
- */
- public void setName(String name) {
- this.name = name;
- }
- /**
- * @return the password
- */
- public String getPassword() {
- return password;
- }
- /**
- * @param password the password to set
- */
- public void setPassword(String password) {
- this.password = password;
- }
- /**
- * @return the type
- */
- public String getType() {
- return type;
- }
- /**
- * @param type the type to set
- */
- public void setType(String type) {
- this.type = type;
- }
- /**
- * @return the url
- */
- public String getUrl() {
- return url;
- }
- /**
- * @param url the url to set
- */
- public void setUrl(String url) {
- this.url = url;
- }
- /**
- * @return the username
- */
- public String getUsername() {
- return username;
- }
- /**
- * @param username the username to set
- */
- public void setUsername(String username) {
- this.username = username;
- }
- }
- -----------------------------------------------------
- ParseDSConfig.java
- -----------------------------------------------------
- /**
- * 操作配置文件类 读 写 修改 删除等操作
- */
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.util.List;
- import java.util.Vector;
- import java.util.Iterator;
- import org.jdom.Document;
- import org.jdom.Element;
- import org.jdom.JDOMException;
- import org.jdom.input.SAXBuilder;
- import org.jdom.output.Format;
- import org.jdom.output.XMLOutputter;
- public class ParseDSConfig {
- /**
- * 构造函数
- */
- public ParseDSConfig() {
- // TODO Auto-generated constructor stub
- }
- /**
- * 读取xml配置文件
- * @param path
- * @return
- */
- public Vector readConfigInfo(String path)
- {
- String rpath=this.getClass().getResource("").getPath().substring(1)+path;
- Vector dsConfig=null;
- FileInputStream fi = null;
- try
- {
- fi=new FileInputStream(rpath);//读取路径文件
- dsConfig=new Vector();
- SAXBuilder sb=new SAXBuilder();
- Document doc=sb.build(fi);
- Element root=doc.getRootElement();
- List pools=root.getChildren();
- Element pool=null;
- Iterator allPool=pools.iterator();
- while(allPool.hasNext())
- {
- pool=(Element)allPool.next();
- DSConfigBean dscBean=new DSConfigBean();
- dscBean.setType(pool.getChild("type").getText());
- dscBean.setName(pool.getChild("name").getText());
- System.out.println(dscBean.getName());
- dscBean.setDriver(pool.getChild("driver").getText());
- dscBean.setUrl(pool.getChild("url").getText());
- dscBean.setUsername(pool.getChild("username").getText());
- dscBean.setPassword(pool.getChild("password").getText());
- dscBean.setMaxconn(Integer.parseInt(pool.getChild("maxconn").getText()));
- dsConfig.add(dscBean);
- }
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (JDOMException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- finally
- {
- try {
- fi.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- return dsConfig;
- }
- /**
- *修改配置文件 没时间写 过段时间再贴上去 其实一样的
- */
- public void modifyConfigInfo(String path,DSConfigBean dsb) throws Exception
- {
- String rpath=this.getClass().getResource("").getPath().substring(1)+path;
- FileInputStream fi=null; //读出
- FileOutputStream fo=null; //写入
- }
- /**
- *增加配置文件
- *
- */
- public void addConfigInfo(String path,DSConfigBean dsb)
- {
- String rpath=this.getClass().getResource("").getPath().substring(1)+path;
- FileInputStream fi=null;
- FileOutputStream fo=null;
- try
- {
- fi=new FileInputStream(rpath);//读取xml流
- SAXBuilder sb=new SAXBuilder();
- Document doc=sb.build(fi); //得到xml
- Element root=doc.getRootElement();
- List pools=root.getChildren();//得到xml子树
- Element newpool=new Element("pool"); //创建新连接池
- Element pooltype=new Element("type"); //设置连接池类型
- pooltype.setText(dsb.getType());
- newpool.addContent(pooltype);
- Element poolname=new Element("name");//设置连接池名字
- poolname.setText(dsb.getName());
- newpool.addContent(poolname);
- Element pooldriver=new Element("driver"); //设置连接池驱动
- pooldriver.addContent(dsb.getDriver());
- newpool.addContent(pooldriver);
- Element poolurl=new Element("url");//设置连接池url
- poolurl.setText(dsb.getUrl());
- newpool.addContent(poolurl);
- Element poolusername=new Element("username");//设置连接池用户名
- poolusername.setText(dsb.getUsername());
- newpool.addContent(poolusername);
- Element poolpassword=new Element("password");//设置连接池密码
- poolpassword.setText(dsb.getPassword());
- newpool.addContent(poolpassword);
- Element poolmaxconn=new Element("maxconn");//设置连接池最大连接
- poolmaxconn.setText(String.valueOf(dsb.getMaxconn()));
- newpool.addContent(poolmaxconn);
- pools.add(newpool);//将child添加到root
- Format format = Format.getPrettyFormat();
- format.setIndent("");
- format.setEncoding("utf-8");
- XMLOutputter outp = new XMLOutputter(format);
- fo = new FileOutputStream(rpath);
- outp.output(doc, fo);
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (JDOMException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- finally
- {
- }
- }
- /**
- *删除配置文件
- */
- public void delConfigInfo(String path,String name)
- {
- String rpath=this.getClass().getResource("").getPath().substring(1)+path;
- FileInputStream fi = null;
- FileOutputStream fo=null;
- try
- {
- fi=new FileInputStream(rpath);//读取路径文件
- SAXBuilder sb=new SAXBuilder();
- Document doc=sb.build(fi);
- Element root=doc.getRootElement();
- List pools=root.getChildren();
- Element pool=null;
- Iterator allPool=pools.iterator();
- while(allPool.hasNext())
- {
- pool=(Element)allPool.next();
- if(pool.getChild("name").getText().equals(name))
- {
- pools.remove(pool);
- break;
- }
- }
- Format format = Format.getPrettyFormat();
- format.setIndent("");
- format.setEncoding("utf-8");
- XMLOutputter outp = new XMLOutputter(format);
- fo = new FileOutputStream(rpath);
- outp.output(doc, fo);
- } catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (JDOMException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- finally
- {
- try {
- fi.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- /**
- * @param args
- * @throws Exception
- */
- public static void main(String[] args) throws Exception {
- // TODO Auto-generated method stub
- ParseDSConfig pd=new ParseDSConfig();
- String path="ds.config.xml";
- pd.readConfigInfo(path);
- //pd.delConfigInfo(path, "tj012006");
- DSConfigBean dsb=new DSConfigBean();
- dsb.setType("oracle");
- dsb.setName("yyy004");
- dsb.setDriver("org.oracle.jdbc");
- dsb.setUrl("jdbc:oracle://localhost");
- dsb.setUsername("sa");
- dsb.setPassword("");
- dsb.setMaxconn(1000);
- pd.addConfigInfo(path, dsb);
- pd.delConfigInfo(path, "yyy001");
- }
- }
- --------------------------------------
- ds.config.xml 配置文件
- --------------------------------------
- <ds-config>
- <pool>
- <type>mysql</type>
- <name>user</name>
- <driver>com.mysql.jdbc.driver</driver>
- <url>jdbc:mysql://localhost:3306/user</url>
- <username>sa</username>
- <password>123456</password>
- <maxconn>100</maxconn>
- </pool>
- <pool>
- <type>mysql</type>
- <name>user2</name>
- <driver>com.mysql.jdbc.driver</driver>
- <url>jdbc:mysql://localhost:3306/user2</url>
- <username>sa</username>
- <password>1234</password>
- <maxconn>10</maxconn>
- </pool>
- <pool>
- <type>sql2000</type>
- <name>books</name>
- <driver>com.microsoft.sqlserver.driver</driver>
- <url>jdbc:sqlserver://localhost:1433/books:databasename=books</url>
- <username>sa</username>
- <password></password>
- <maxconn>100</maxconn>
- </pool>
- </ds-config>
1).Connection的获得和释放
DBConnectionManager connectionMan=DBConnectionManager .getInstance();//得到唯一实例
//得到连接
String name="mysql";//从上下文得到你要访问的数据库的名字
Connection con=connectionMan.getConnection(name);
//使用
。。。。。。。
// 使用完毕
connectionMan.freeConnection(name,con);//释放,但并未断开连接
2).数据库连接的动态增加和连接池的动态增加
1.调用xml操作增加类
2.重新实例华连接池管理池类
- Java jdbc数据库连接池总结!
- Java jdbc数据库连接池总结!
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结
- Java JDBC数据库连接池总结
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结!
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结
- Java jdbc数据库连接池总结!
- Java jdbc数据库连接池总结!
- Java jdbc数据库连接池总结!
- Java jdbc数据库连接池总结!
- 备份恢复MySQL数据库的命令
- 撒吃饭圣达菲
- stock协议
- @OneToMany @ManyToMany 关联表的不同
- Java反射机制
- Java jdbc数据库连接池总结
- 将例3.13中程序中的display函数不放在Time类中(第三章第10题)
- IP与物理地址
- struts2中使用jquery异步加载整个页面
- TJU 2248 Channel Design (最小树形图-朱刘算法)
- 意外发现了winform的一个控件个数的极限
- JNDI+Tomcat配置数据源的两种方式
- KNN算法简单介绍
- An problem about date