精华内容
下载资源
问答
  •  一般应用系统数据库访问模块可大致分为两层,一层是对数据库连接、连接池和结果集等直接对数据库的操作的封装,由于libmysql提供的库函数是直接操作数据库的,所以这一层在本质上是直接操作数据库的一层;...

    1、  一般架构说明

    1 架构层次图

            一般应用系统数据库访问模块可大致分为两层,一层是对数据库连接、连接池和结果集等直接对数据库的操作的封装,由于libmysql提供的库函数是直接操作数据库的,所以这一层在本质上是直接操作数据库的一层;二是可以根据系统业务功能将系统与数据库的交互划分为几个子块,提供给系统其它模块与数据库交互的接口。如果是C/S结构的系统,客户端与数据库的交互还可以通过诸如RPC(Remote Procedure Call Protocol 远程过程调用协议)等协议调用服务端RPC处理模块来进行。

            如上的设计一是隔离了外界与数据库的直接交互,提高了数据安全性;二是对libmysql提供的数据库操作接口众多,统一类操作有多种不同的方式,各自适用的场合不同,对其进行了整合,是为使其更适合本系统,提高了系统的稳定性和可用性,同时使上层对数据库的操作更为方便;三是按系统功能划分子模块降低了系统的耦合度,提高了系统的可扩展性。

            另外,数据库直接交互层也分为三个小块,这样做的目的也是为了降低系统耦合度。其中数据库连接管理块仅负责数据库连接的维持及相应查询事物处理。数据库连接池管理块则是负责在系统初始化时创建一定数量的数据库连接,实际上就是建立两个连接队列(一个在用连接队列和一个空闲连接队列)并维护这两个队列。对于连接池的建立是为了避免在每一次操作数据库时都要建立数据库连接和释放数据库连接等耗时操作,提高系统性能。数据结果集的处理是专门负责将查询返回的结果按上层要求的方式提供给上层使用。

    2、  数据库连接类的实现

            本类主要是实现数据库连接的建立和释放,数据库选择,SQL语句的执行,事务的提交和回滚及数据库错误信息的获取等功能。其中数据库连接的建立与释放及查询语句的执行是本节叙述的重点。在libmysql提供的库中使用mysql_real_connect()可与MySQL数据库建立连接,但在这之前还需要对MYSQL实例进行初始化,其中MYSQL为MySQL的数据库连接句柄,具体实现过程如下。

            a、使用mysql_init()创建MYSQL句柄实例;

            b、根据数据库主机地址、用户名、用户密码、数据库名及端口号等连接信息使用mysql_real_connect()接口为MYSQL句柄实例建立数据库连接;

            c、为已经建立数据库连接的MYSQL句柄实例选择字符集。

            需要注意的是,当应用程序服务器和数据库服务器不在同一主机时,新安装的MySQL数据库处于安全考虑是不允许远程连接的,这就需要为相应的用户赋予远程访问的权限,可采用MySQL命令:
    GRANT EXECUTE ON DBName.TableName TO 'UserName'@'192.168.1.1' IDENTIFIED BY 'UserPassword';

            其中“192.168.1.1”为远程主机的IP地址,使用“%”表示接受所有IP主机的远程访问。当然出于数据安全考虑,需要慎重执行。最终实现连接建立的代码如下:
    int CDBConnect::ConnectDB(const char *host, const char *user, const char *password, const char *db, unsigned int port)
    {
    	if (m_pMySql != NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Trace( __FILE__, __LINE__, "m_pMySql 已经初始化\n");
    		}
    		return DATABASE_NOERROR;
    	}
    
    	m_pMySql = mysql_init((MYSQL*)NULL);
    
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Fatal( __FILE__, __LINE__, "m_pMySql 初始化失败\n");
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	
    	if (!mysql_real_connect(m_pMySql, host, user, password, db, port,NULL,0))
    	{
    		if( NULL != pLC )
    		{
    			pLC->Fatal( __FILE__, __LINE__, "数据库连接失败,%s\n", GetLastErrorMsg());
    		}
    		int iRet = mysql_errno(m_pMySql);
    		printf( "errno = %d\n", iRet );
    		if (iRet == 1045)
    		{  
    			return DATABASE_ERROR_USERORPASSWORD;
    		}
    		return DATABASE_ERROR_GET_CONNECTION;
    	}
    
    	if (mysql_set_character_set(m_pMySql, "gbk") != 0) 
    	{
    		if( NULL != pLC )
    		{
    			pLC->Error( __FILE__, __LINE__, "数据库字符集设置失败,%s\n", GetLastErrorMsg());
    		}
    		return DATABASE_POOL_ERROR_SET_CHARACTER;
    	}
    
    	return DATABASE_NOERROR;
    }
    
    void CDBConnect::Release()
    {
    	delete this;
    }
    
    void CDBConnect::CloseConnect()
    {
    	m_ResultSet.Close();
    
    	if (m_pMySql != NULL)
    	{
    		mysql_close(m_pMySql);
    		m_pMySql = NULL;
    	}
    }
    
    void CDBConnect::CloseQuery()
    {
    	m_ResultSet.Close();
    }
    
    int CDBConnect::ConnctionTest()
    {
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	else
    	{
    		return mysql_ping(m_pMySql); 
    	}
    }
    
    int CDBConnect::SelectDB(const char *szDB)
    {
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	if (mysql_select_db(m_pMySql,szDB))
    	{
    		if( NULL != pLC )
    		{
    			pLC->Error( __FILE__, __LINE__, "选择数据库失败,%s\n", GetLastErrorMsg());
    		}
    		return -1/*DATABASE_POOL_ERROR_SELECT_DATABASE*/;
    	}
    	else
    	{
    		return DATABASE_NOERROR;
    	}
    }
    
    int CDBConnect::AutoCommit(bool mode)
    {
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	return mysql_autocommit(m_pMySql, mode);
    }
    
    int CDBConnect::Commite()
    {
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	return mysql_commit(m_pMySql); 
    }
    
    int CDBConnect::RollBack()
    {
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	return mysql_rollback(m_pMySql); 
    }
    
    PIDBResultSet CDBConnect::ExcuteQuery(const char *szSql)
    {
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
    		}
    		return NULL;
    	}
    	if(mysql_query(m_pMySql, szSql))
    	{
    		if( NULL != pLC )
    		{
    			pLC->Error( __FILE__, __LINE__, "读数据库失败,%s\n", GetLastErrorMsg());
    		}
    		return NULL;
    	}
    	m_ResultSet.Reset(mysql_store_result(m_pMySql));	
    	return &m_ResultSet;
    }
    
    int CDBConnect::ExcuteSql(const char *szSql)
    {
    	if (m_pMySql == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	if(mysql_query(m_pMySql,szSql))
    	{
    		if( NULL != pLC )
          	{
             	pLC->Error( __FILE__, __LINE__, "SQL语句执行失败,%s\n", GetLastErrorMsg());
    		}
    		return DATABASE_ERROR_EXCUTE_QUERY;
    	}
    	return mysql_affected_rows(m_pMySql);
    }
    
    
    int CDBConnect::ExcuteRealSql(const char *szSql, unsigned long ulSqlLen )
    {
    	//这里添加参数检查语句
    	if( ulSqlLen <= 0 )
    	{
    		//这里添加日志打印语句
    
    	}
    	if (m_pMySql == NULL)
    	{
    		//这里添加日志打印语句
    		return DATABASE_ERROR_EXCUTE_QUERY;
    	}
    	if(mysql_real_query(m_pMySql,szSql, ulSqlLen))
    	{
    		//这里添加日志打印语句
    		return DATABASE_ERROR_EXCUTE_QUERY;
    	}
    	return mysql_affected_rows(m_pMySql);
    }
    
    const char* CDBConnect::GetLastErrorMsg()
    {
    	if (m_pMySql != NULL)
    	{
    		return mysql_error(m_pMySql);
    	}
    	else
    	{
    		return NOT_CONNECT_DATABASE;
    	}
    }
    
    int CDBConnect::GetLastInsertID(int *pInsertID)
    {
    	if (m_pMySql != NULL)
    	{
    		*pInsertID = mysql_insert_id(m_pMySql);
    
    		return DATABASE_NOERROR;
    	}
    	else
    	{
          if( NULL != pLC )
          {
             pLC->Debug( __FILE__, __LINE__, "m_pMySql 没有初始化\n");
          }
    
    		return DATABASE_ERROR_INIT_POOL;
    	}
    }
    
    unsigned long CDBConnect::EscapeRealToString(  char *pDst, const char *pSrc, unsigned long ulSrcLen )
    {
    	//这里添加参数检查代码…
    	if( ulSrcLen <= 0 )
    	{
    		//这里添加日志打印语句
    	}
    
    	if (m_pMySql == NULL)
    	{
    		//这里添加日志打印语句
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	return mysql_real_escape_string(m_pMySql,pDst, pSrc, ulSrcLen);
    }

     3、  数据库连接池类的实现

            本类实现了连接池的建立和销毁,连接池运行状态信息的获取,从连接池中取出可用数据库连接及将短时间内不再使用的数据库连接放回连接池等操作。本类的架构实现关系到数据库的访问速度,攸关整个系统性能。同时为了维护连接池的安全性,保证在同一时间内只有一个线程能操作连接池,同一个连接也只能被一个线程使用,涉及到设置临界区和信号量等操作,下面将逐一详述。

            a、数据结构描述
    typedef std::list<PIDBConnect> CONNECTION_HANDLE_LIST;
    typedef std::list<PIDBConnect>::iterator ITER_CONNECTION_HANDLE_LIST;
    class CDBConnectPool
    {
    private:
    string m_host;           //主机
    string m_user;           //用户名
    string m_password;       //密码
    string m_db;             //数据库名
    unsigned int m_port;     //端口
    unsigned int m_connNum;  //连接池的大小
          
    CONNECTION_HANDLE_LIST m_lsBusyList; //正在使用的连接句柄,队列模型
    CONNECTION_HANDLE_LIST m_lsIdleList; //未使用的连接句柄,队列模型
    		
    CRITICAL_SECTION m_csList;            //临界值句柄
    HANDLE           m_hSemaphore;        //信号量句柄
    static CDBConnectPool *pConnPool;     //单例模式
    }

            b、连接池的建立

            在连接池建立时,首先应跟据用户要求建立连接的数量创建相应数量信号量,在逐个新建连接并放入空闲连接队列,其中任何一个连接创建失败都将导致整个连接池的建立失败。具体实现如下:
    int CDBConnectPool::Connect()
    {
    	//同时打开connNum个连接
    	int ret = 0;
    	if (m_hSemaphore != NULL)
    	{
    		return DATABASE_ERROR;
    	}
    	m_hSemaphore = CreateSemaphore(NULL, m_connNum, m_connNum, NULL);
    	if (m_hSemaphore == NULL)
    	{
    		return DATABASE_POOL_ERROR_INIT_SEMAPHORE;
    	}
       int i = 0;
    	for ( i=0; i<m_connNum; ++i)
    	{
    		CDBConnect *pConn = new CDBConnect();
    		ret = pConn->ConnectDB(m_host.c_str(), m_user.c_str(), m_password.c_str(), m_db.c_str(), m_port);
    		if(ret<0)
    		{
    			break;
    		}
          	m_lsIdleList.push_back(pConn);
    	}
    	if (i != m_connNum)
    	{
    		Close();
    		if (ret == DATABASE_ERROR_USERORPASSWORD)
    		{
    			return ret;
    		}
    		return DATABASE_ERROR_INIT_POOL;
    	}
    	return DATABASE_NOERROR;
    }

            c、关闭连接池

            连接池的关闭与创建过程恰好相反,首先应关闭所有数据库连接,包括在用的和未用的,再清空队列(空闲连接队列和在用连接队列),最后清除信号量。实现代码如下:
    void CDBConnectPool::Close()
    {
    	ITER_CONNECTION_HANDLE_LIST iter;
    	for (iter = m_lsBusyList.begin(); iter != m_lsBusyList.end(); iter++)
    	{
    		PIDBConnect pConn = *iter;
    		if( pConn != NULL )
    		{
    			pConn->CloseConnect();
    			pConn->Release();
    		}
    	}
    	m_lsBusyList.clear();	
    	for (iter = m_lsIdleList.begin(); iter != m_lsIdleList.end(); iter++)
    	{
    		PIDBConnect pConn = *iter;
    		if( pConn != NULL )
    		{
    			pConn->CloseConnect();
    			pConn->Release();
    		};
    	}
    	m_lsIdleList.clear();
    	if( m_hSemaphore != NULL )
    	{
    		CloseHandle(m_hSemaphore);
    		m_hSemaphore = NULL;
    	}
    }

            d、从连接池中获取可用连接

            在从连接池中取出连接时,首先等待信号量,直到有可用连接或者等待超时返回(避免线程死锁),然后进入临界区,即对两个队列的操作区域,防止在取连接时有其它线程也来去连接或是放连接回池,接着离开临界区,通知其它等待线程可操作临界区。另外比较重要的一点是,为确保我们取到的连接是真正可用的连接,在从空闲连接队列中取出连接并放入忙连接队列后需要测试我们得到的连接是否连通,未连通时应尝试重连,若重连依然失败,则应返回空。实现代码如下:
    PIDBConnect CDBConnectPool::GetConnection(int iTimeOut)
    {
       int ret;
    	PIDBConnect pConn = NULL;
    	DWORD dwWaitResult;
    
    	dwWaitResult = WaitForSingleObject(m_hSemaphore, iTimeOut);
    	if (dwWaitResult != WAIT_OBJECT_0)
    	{
    		return NULL;
    	}
    	EnterCriticalSection(&m_csList);
    	if (m_lsIdleList.size() > 0)
    	{
    		pConn = m_lsIdleList.front();
    		m_lsIdleList.pop_front();
    		m_lsBusyList.push_back(pConn);
    	}
    	else
    	{
    		pConn = NULL;
    	}
    	LeaveCriticalSection(&m_csList);
    
    	if (pConn->ConnctionTest() != 0)
       {
          char strError[1000] = {0};
          strncpy(strError, pConn->GetLastErrorMsg(), 999);
          strError[999] = '\0';
          pConn->CloseConnect();
          ret = pConn->ConnectDB(m_host.c_str(), m_user.c_str(), 
    						m_password.c_str(), m_db.c_str(), m_port);
          if (ret < 0)
          {
             BackToPool(pConn);
             return NULL;
          }
       }
    	return pConn;
    }

            e、将未用连接放回连接池
    void CDBConnectPool::BackToPool(PIDBConnect pConn)
    {
    	pConn->CloseQuery();
        pConn->AutoCommit(true);
    	EnterCriticalSection(&m_csList);
    	ReleaseSemaphore(m_hSemaphore, 1, NULL); 
    	m_lsBusyList.remove(pConn);
    	m_lsIdleList.push_back(pConn);
    	LeaveCriticalSection(&m_csList);
    }

    4、  数据库结果集类的实现

            本类主要实现对查询返回结果的一系列操作,包含查找指定数据、取得记录数、获得下一条记录及根据字段名或是字段在结果集中的ID获取字段等。直接给出源码如下:

    头文件:
    /
    /// Copyright (C), 2011
    /// \file  DBResultSet.h
    /// \brief DATABASE数据库结果集接口
    /// \author hkp(horace20@live.cn) 
    /// \version 1.1
    /// \date    2011
    ///
    
    
    #ifndef _DATABASE_RECORD_SET_
    #define _DATABASE_RECORD_SET_
    
    #pragma comment(lib,"libmysql.lib")
    #pragma warning(disable: 4786)     ///<Disable warning messages
    
    #include <string>
    #include <map>
    
    using namespace std;
    
    namespace Database
    {
    	class CDBResultSet : public IDBResultSet
    	{
    		friend class CDBConnect;    ///<友元类
          
    	private:
    		MYSQL_RES*   m_pMyQuery;    ///<结果集
    		MYSQL_ROW    m_currentRow;  ///<基类
    		unsigned int m_fieldCount;  ///<字段的个数
    
    	private:
    		/
    		/// \brief CDBResultSet类的构造函数
    		/// \param[in]  无
    		/// \param[out] 无
    		/// \return     无
    		/
    		CDBResultSet();
    
    		/
    		/// \brief 释放结果集,得到字段的个数
    		/// \param[in]  结果集
    		/// \param[out] 无
    		/// \return     无
    		/      
    		void Reset(MYSQL_RES *pMyQuery);
    
    	public:
    		/
    		/// \brief CDBResultSet类的析构函数
    		/// \param[in]  结果集
    		/// \param[out] 无
    		/// \return     无
    		/      
    		~CDBResultSet(){ Close(); }
    
    		/
    		/// \brief 是否最后
    		/// \param[in] 无
    		/// \param[out] 无
    		/// \return     TRUE 到最后  FALSE 没到最后 
    		/      		
    		bool IsEnd();                
          
    		/
    		/// \brief 查找指定数据
    		/// \param[in] 偏移量
    		/// \param[out] 无
    		/// \return     无
    		/      		
    		void SeekData(int offset);  
          
    		/
    		/// \brief 释放结果集
    		/// \param[in] 无
    		/// \param[out] 无
    		/// \return     无
    		/      		
     		void Close();                         
    
    		/
    		/// \brief 得到记录数
    		/// \param[in] 无
    		/// \param[out] 无
    		/// \return     >=0 正常  <0异常
    		/      		
    		DB_ROW_CONUT GetRowNum();                      
    
    		/
    		/// \brief 取得下一条记录
    		/// \param[in] 无
    		/// \param[out] 无
    		/// \return     TRUE 正常  FALSE 异常
    		/      		
    		bool GetNextRecod();
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得有符号整型值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, int& value);           
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得无符号整型值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, unsigned int& value);   
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得字符串类型值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, string& value);     
          
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得len长度的字符串值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, char* value, int len);  
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得bool值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, bool& value); 
          
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得float值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, float& value);  
          
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得double值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, double& value);        
          
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得时间类型值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(int filedId, time_t& value);        
    
    		/
    		/// \brief 得到字段,此函数分配内存,调用FreeGetFiledByte(BYTE **value)释放内存
    		/// \param[in] 字段下标
    		/// \param[out] 指向二进制buffer的指针
    		/// \param[out] 指针的长度
    		/// \return     =0 正常 <0异常
    		/
            int GetFiled(int filedId, uint8 **value, int *len);
        	
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段名
    		/// \param[out] 取得有符号整型值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(const char* fieldName, int& value);            
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段下标
    		/// \param[out] 取得wu符号整型值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(const char* fieldName, unsigned int& value); 
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段名
    		/// \param[out] 取得字符串值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(const char* fieldName, string& value);
          
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段名
    		/// \param[out] 取得len长度的字符串值
    		/// \return     =0 正常 <0异常
    		/      		
    		int GetFiled(const char* fieldName, char* value, int len); 
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段名
    		/// \param[out] 取得bool值
    		/// \return     =0 正常 <0异常
    		/  
    		int GetFiled(const char* fieldName, bool& value);  
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段名
    		/// \param[out] 取得float值
    		/// \return     =0 正常 <0异常
    		/  
    		int GetFiled(const char* fieldName, float& value);         
    
    		/
    		/// \brief 得到字段
    		/// \param[in] 字段名
    		/// \param[out] 取得double值
    		/// \return     =0 正常 <0异常
    		/  
    		int GetFiled(const char* fieldName, double& value);    
          
    		/
    		/// \brief 得到字段,此函数分配内存,调用FreeGetFiledByte(BYTE **value)释放内存
    		/// \param[in]  字段下标
    		/// \param[out] 指向二进制buffer的指针
    		/// \param[out] 指针的长度
    		/// \return     =0 正常 <0异常
    		/
    		int GetFiled(const char* fieldName, time_t& value);     
    
    		/
    		/// \brief 得到字段,此函数分配内存,调用FreeGetFiledByte(BYTE **value)释放内存
    		/// \param[in] 字段名
    		/// \param[out] 指向二进制buffer的指针
    		/// \param[out] 指针的长度
    		/// \return     =0 正常 <0异常
    		/
            int GetFiled(const char* fieldName, uint8 **value, int *len);
    
    	private:
          
    		/
    		/// \brief 得到字段ID
    		/// \param[in] 字段名
    		/// \param[out] 取得字段的ID
    		/// \return     >=0 正常 <0异常
    		/   
    		int GetFiedIdByName(const char* fieldName);
    	};
    }
    #endif

    CPP文件:
    CDBResultSet::CDBResultSet()
    {
    	m_pMyQuery   = NULL;
    	m_currentRow = NULL;
    	m_fieldCount = 0;
    }
    
    void CDBResultSet::Reset(MYSQL_RES *pMyQuery)
    {
    	Close();
    	m_pMyQuery   = pMyQuery;
    	m_currentRow = NULL;
    	if (pMyQuery != NULL)
    	{
    		m_fieldCount = mysql_num_fields(pMyQuery);
    	}
    	else
    	{
    		if( NULL != pLC )
    		{
    			pLC->Trace( __FILE__, __LINE__, "pMyQuery 未初始化\n");
    		}
    		m_fieldCount = 0;
    	}
    }
    
    bool CDBResultSet::IsEnd()
    {
    	if (m_pMyQuery == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
    		}
    		return false;
    	}
    	return mysql_eof(m_pMyQuery) != 1;
    }
    
    void CDBResultSet::SeekData(int offset)
    {
    	if (m_pMyQuery == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
    		}
    		return;
    	}
    	mysql_data_seek(m_pMyQuery, offset);
    }
    
    void CDBResultSet::Close()
    {
    	if (m_pMyQuery == NULL)
    	{
    		return;
    	}
    	mysql_free_result(m_pMyQuery);
    	m_pMyQuery   = NULL;
    	m_currentRow = NULL;
    	m_fieldCount = 0;
    }
    
    bool CDBResultSet::GetNextRecod()
    {
    	if (m_pMyQuery == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
    		}
    		return false;
    	}
    	if( (m_currentRow = mysql_fetch_row(m_pMyQuery)) != NULL)
    	{
    		return true;
    	}
    	else
    	{
    		if( NULL != pLC )
    		{
    			pLC->Warn( __FILE__, __LINE__, "m_currentRow 为空\n");
    		}
    		return false;
    	}
    }
    
    DB_ROW_CONUT CDBResultSet::GetRowNum()
    {
    	if (m_pMyQuery == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化\n");
    		}
    		return DATABASE_NOERROR;
    	}
    	return mysql_num_rows(m_pMyQuery);
    }
    
    int CDBResultSet::GetFiled(int filedId, int& value)
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	char* pField = m_currentRow[filedId];
    	if (pField == NULL || pField[0] == 0)
    	{
    		value = 0;
    		return DATABASE_ERROR_NEXT_ROW;
    	}
    	value = atoi(pField);
    	return DATABASE_NOERROR;
    }
    
    int CDBResultSet::GetFiled(int filedId, unsigned int& value)
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	char* pField = m_currentRow[filedId];
    	if (pField == NULL || pField[0] == 0)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Trace( __FILE__, __LINE__, "字段为空\n");
    		}
    		value = 0;
    		return DATABASE_ERROR_NEXT_ROW;
    	}
    	value = atoi(pField);
    	return DATABASE_NOERROR;
    }
    
    int CDBResultSet::GetFiled(int filedId, string& value)
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	char* pField = m_currentRow[filedId];
    	value = pField;
    	return DATABASE_NOERROR;
    }
    
    int CDBResultSet::GetFiled(int filedId, char* value, int len)  
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount || value == NULL || len < 0)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	if (m_currentRow[filedId] == NULL)
    	{
    		value[0] = '\0';
    	}
    	else
    	{
    		strncpy(value, m_currentRow[filedId], len-1);
    		value[len-1] = '\0';
    	}
    	return DATABASE_NOERROR;
    }
    
    int CDBResultSet::GetFiled(int filedId, bool& value)
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	char* pField = m_currentRow[filedId];
    	if (pField == NULL || pField[0] == 0)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Warn( __FILE__, __LINE__, "字段为空\n");
    		}
    		value = false;
    		return DATABASE_ERROR_NEXT_ROW;
    	}
    	value = atoi(pField) != 0;
    	return DATABASE_NOERROR;
    }
    
    int CDBResultSet::GetFiled(int filedId, float& value)
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	char* pField = m_currentRow[filedId];
    	if (pField == NULL || pField[0] == 0)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Warn( __FILE__, __LINE__, "字段为空\n");
    		}
    		value = 0.0;
    		return DATABASE_ERROR_NEXT_ROW;
    	}
    	value = atof(pField);
    	return DATABASE_NOERROR;
    }
    
    int CDBResultSet::GetFiled(int filedId, double& value)
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	char* pField = m_currentRow[filedId];
    	if (pField == NULL || pField[0] == 0)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Warn( __FILE__, __LINE__, "字段为空\n");
    		}
    		value = 0.0;
    		return DATABASE_ERROR_NEXT_ROW;
    	}
    	value = atof(pField);
    	return DATABASE_NOERROR;
    }
    
    int CDBResultSet::GetFiled(int filedId, time_t& value)
    {
    	if (m_currentRow == NULL || filedId>=m_fieldCount)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	char* pField = m_currentRow[filedId];
    	tm time;
    	ZeroMemory(&time, sizeof(time));
    	if (pField == NULL || pField[0] == 0)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Warn( __FILE__, __LINE__, "字段为空\n");
    		}
    		value = 0;
    		return DATABASE_ERROR_NEXT_ROW;
    	}
    	sscanf(pField, "%d-%d-%d %d:%d:%d", &time.tm_year, &time.tm_mon, &time.tm_mday, &time.tm_hour, &time.tm_min, &time.tm_sec);
    	time.tm_year -= 1900;
    	time.tm_mon  -= 1;
    	value = mktime(&time);
    	return DATABASE_NOERROR;
    }
    
    ///<函数功能:得到字段
    int CDBResultSet::GetFiled(int filedId, uint8 **value, int *len)
    {
       *len = 0;
       *value = NULL;
       if (m_currentRow == NULL || filedId>=m_fieldCount)
       {
          if( NULL != pLC )
          {
             pLC->Debug( __FILE__, __LINE__, "m_currentRow 未初始化或下标越界\n");
          }
          return DATABASE_ERROR_GET_FIELDS;
       }
    
       if (m_currentRow[filedId] == NULL)
       {
          if( NULL != pLC )
          {
             pLC->Trace( __FILE__, __LINE__, "此字段为空\n");   
             *len = 0;  
             //*value = new uint8[1];
             *value = (uint8 *)DATABASE_ALLOC(sizeof(uint8));
             if (*value == NULL)
             {
                if( NULL != pLC )
                {
                   pLC->Trace( __FILE__, __LINE__, "分配内存失败\n");
                }    
                return DATABASE_ERROR_ALLOC_BUFFER;
             }
             (*value)[0] = '\0';
          }
       }
       else
       {
    
          unsigned long * plLen = mysql_fetch_lengths(m_pMyQuery); //读该字段的长度,含0值数据
          *len = (int)*plLen;
          *value = (uint8 *)DATABASE_ALLOC(sizeof(uint8)*(*len) + 1);
          if (*value == NULL)
          {
             *len = 0;
             if( NULL != pLC )
             {
                pLC->Trace( __FILE__, __LINE__, "分配内存失败\n");
             }      
    
             return DATABASE_ERROR_ALLOC_BUFFER;
          }
          
          for (int i = 0; i < *len; i++)
          {
             (*value)[i] = (uint8)m_currentRow[filedId][i];
          }
    
          (*value)[*len] = '\0';
       }
    
        return DATABASE_NOERROR;
    }           
    
    
    int CDBResultSet::GetFiedIdByName(const char* fieldName)
    {
    	if (m_pMyQuery == NULL)
    	{
    		if( NULL != pLC )
    		{
    			pLC->Debug( __FILE__, __LINE__, "m_pMyQuery 未初始化\n");
    		}
    		return DATABASE_ERROR_GET_FIELDS;
    	}
    	for (int i=0; i<m_fieldCount; i++)
    	{
    		MYSQL_FIELD *pField = mysql_fetch_field_direct(m_pMyQuery, i);
    		if (pField != NULL && strcmp(pField->name, fieldName) == 0)
    		{
    			return i;
    		}
    	}
    	if( NULL != pLC )
    	{
    		pLC->Trace( __FILE__, __LINE__, "未找到该字段\n");
    	}
    
    	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, int& value)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value);
    	}
    
    	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, unsigned int& value)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value);
    	}
    
    	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, string& value)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value);
    	}
    
     	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, char* value, int len)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value, len);
    	}
    
     	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, bool& value)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value);
    	}
    
    	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, float& value)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value);
    	}
    
       return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, double& value)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value);
    	}
    
    	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, time_t& value)
    {
    	int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value);
    	}
    
     	return DATABASE_ERROR_NEXT_ROW;
    }
    
    int CDBResultSet::GetFiled(const char* fieldName, uint8 **value, int *len)
    {
       int id = 0;
    	if ((id=GetFiedIdByName(fieldName)) >= 0)
    	{
    		return GetFiled(id, value, len);
    	}
    
        return DATABASE_NOERROR;
    } 

    5、  总结

            总结就不必了,本文出自个人本科毕设摘抄整理,未经允许不得转载,如需转载请联系horace20@live.cn,如若学校说我论文是抄别人的,那我就冤枉了……
    展开全文
  • 概述写程序的时候,我们经常要获取数据库连接对象(java.sql.Connection),一般我们可以直接通过DriverManager获取数据库连接对象,也可以通过...正文创建数据库连接对象(java.sql.Connection)的类包(DbConnection.ja

    概述

    写程序的时候,我们经常要获取数据库连接对象(java.sql.Connection),一般我们可以直接通过DriverManager获取数据库连接对象,也可以通过JNDI从部署在应用服务器中的数据库连接池中获取数据库连接对象。如何更加方便灵活的获取数据库连接对象正是本文的写作目的。

    正文

    创建数据库连接对象(java.sql.Connection)的类包(DbConnection.jar)以一个xml配置文件(db-connection-cfg.xml)为基础,该配置文件内容如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <db-connection>
        <db-connect-type>cp</db-connect-type>
        <app-server-name>weblogic</app-server-name>
        <connection-pool>
            <jndi-env>
               <weblogic>
                    <property name="java.naming.provider.url" value="t3://your_application_server_ip:port/application_name"/>
                    <property name="java.naming.factory.initial" value="weblogic.jndi.WLInitialContextFactory"/>
                    <property name="java.naming.security.principal" value="username"/>
                    <property name="java.naming.security.credentials" value="password"/>
                    <used>yes</used>
               </weblogic>
            </jndi-env>
            <datasource-binding-name>jdbc/epdsmdbDS</datasource-binding-name>
        </connection-pool>
        <jdbc>
           <driver-class>oracle.jdbc.driver.OracleDriver</driver-class>
           <url>jdbc:oracle:thin:@your_database_server_ip:1521:service_id</url>
           <username>username</username>
           <password>password</password>
        </jdbc>
    </db-connection>

    下面我说明一下这个配置文件的配置方法

    1.<db-connect-type>元素表示获取数据库连接对象方式:cp表示从连接池中获取Connection,jdbc表示直接创建Connection。

    2.<app-server-name>元素表示部署了数据库连接池的应用服务器名称,类包通过该名称获取JNDI环境信息。当用户设置一种应用服务器名称(如oc4j),那么相应的JNDI环境信息通过在<jndi-env>元素中增加相应的<oc4j><property name="property_name" value="property_value"/><used>yes</used></oc4j>来配置。其中<used>元素的值表示查找数据源对象(javax.sql.DataSource)时是否使用JNDI环境信息,yes代表使用,no代表不使用。提供这个元素是因为部署在Application Server上的应用在从连接池中获取连接时不需要JNDI环境信息,而Standalone Application则需要。

    3.<property>元素表示JNDI环境信息中的一个条目。

    4.<datasource-binding-name>元素表示数据源在JNDI Tree中的名称。

    5.<jdbc>以及其子元素描述了通过DriverManager创建Connection时的信息。

    DbConnection.jar包使用方法

    配置db-connection-cfg.xml,将其和DbConnection.jar包加入应用的classpath,在需要创建Connection的地方使用Connection conn = DbConnectionFactory.getConnection();即可。

    总结

    DbConnection.jar包可供Web Appication及Standalone Application使用。相信能给开发者带来方便。

    展开全文
  • 数据库连接

    千次阅读 2019-10-22 19:54:01
    1、什么是数据库连接池? 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个; 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放...

    1、什么是数据库连接池?

    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;

    释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。

    这项技术能明显提高对数据库操作的性能

    2、连接池解决的问题???

          不断的创建、销毁链接,会导致访问数据库服务器的压力,而且对于内存来说,不断的开辟与销毁,内存的使用率极低。使用数据库连接池可以明显提高对数据库操作的性能!

          数据库连接池的解决方案是在应用程序启动时建立足够的数据库连接,并将这些连接组成一个连接池(简单说:在一个“池”里放了好多半成品的数据库连接对象),由应用程序动态地对池中的连接进行申请、使用和释放。对于多于连接池中连接数的并发请求,应该在请求队列中排队等待并且应用程序可以根据池中连接的使用率,动态增加或减少池中的连接数

    3、数据库连接池的工作原理???

      1、三步骤     连接池的建立,连接池中连接的使用管理,连接池的关闭

    一、连接池的建立:创建几个连接对象,以便使用的时候能从连接池中获取

    二、客户请求数据库连接时,首先查看连接池中是否有空闲连接,若存在,将连接分配给用户使用;如果没有空闲连接,则查看当前所开的连接数是否已经达到最大的连接数,如果没有达到,就重新创建一个连接给请求的客户,如果达到就按照设定的最大等待时间进行等待,超出的最大等待的时间,就抛出异常给用户,当用户释放数据连接的时候,先判断该连接的引用次数是否超过了规定的值,如果超过就删除,如果没有的话,就保留为其他客户服务。保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源的资源开销。

    三、连接池的关闭,当应用程序退出的时候i,关闭所有的连接,释放相关的资源,正好与创建相反

    4、数据库连接配置时的参数(可选,有默认值)

    最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.

    最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作

    最大空闲时间:连接池对象最长的闲置时间,如果长时间没有被使用,那么此连接对象就会被销毁。

    获取连接超时时间:池子中总共有50条连接,当这50条连接都处于工作状态时,第51个连接请求到达,让其加入到等待队列中,等待时间如果超过了设置的超时时间,则抛出异常

    初始化连接数:当数据库连接池创建时,初始化的连接数量

    5、主流的数据库连接池技术

    创建QueryRunner时,可以传递一个数据源DataSource,我们可以将这个数据源理解为数据库连接池,那么我们现在使用不同的数据库连接池技术,目的就只有一个,得到数据源对象

    6、DBCP

    介绍:DBCP(DataBase Connection Pool)属于Apache顶级项目Commons中的核心子项目(最早在Jakarta Commons里就有),它内部依赖于Commons中的另一个子项目Pool,连接池最核心的“池”,就是由Pool组件提供的,因此,DBCP的性能实际上就是Pool的性能

    步骤:

    导包 build path

    书写dbcp.properties属性文件,文件目录在src下

    书写dbcp工具类,来提供数据源

    测试数据源是否可用

    7、封装一个工具类,专门用于向外界提供dbcp的数据源。

    /**
     * 此类的作用就是生成并且提供dbcp的数据源
     * @author Administrator
     *
     */
    public class DBCPUtils {
    	private static DataSource dataSource = null;
    	
    	//完成对数据源的赋值,赋值只需要走一次,所以在静态代码块中完成赋值
    	static {
    		Properties properties = new Properties();
    		FileInputStream fStream = null;
    		try {
    			//创建输入流对象,来读取配置文件dbcp.properties
    			fStream = new FileInputStream("src/dbcp.properties");
    			//将流中读取到的内容加载属性文件对象中
    			properties.load(fStream);
    			//将加载完毕内容的属性文件对象传递到dbcp创建数据源的方法中,生成数据源
    			dataSource = BasicDataSourceFactory.createDataSource(properties);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    	
    	//返回数据源的方法
    	public static DataSource getDataSource() {
    		return dataSource;
    	}
    }

    测试代码的书写:

    public static void main(String[] args) throws SQLException {
    		/*
    		 	使用dbcp数据源来操作数据库
    		 	方式1:在创建QueryRunner对象,给定数据源,此时不需要书写连接获取的代码,会自动从给定的数据源中获取连接
    		 	使用完毕,不需要执行关闭资源的方法,连接对象会自动归还池子
    		 	
    		 	方式2:我们可以通过数据源的getConnection()获取到由池子管理的数据库连接对象
    		 	使用完毕后,执行关闭资源的方法,并不是将连接对象销毁,而是将连接对象归还池子
    		 */
    		/*
    		QueryRunner qRunner = new QueryRunner(DBCPUtils.getDataSource());
    		List<User> users = qRunner.query("select * from user", 
    				new BeanListHandler<User>(User.class));
    		for (User user : users) {
    			System.out.println(user);
    		}*/
    		
    		DataSource dataSource = DBCPUtils.getDataSource();
    		Connection connection = dataSource.getConnection();
    		QueryRunner qRunner = new QueryRunner();
    		List<User> users = qRunner.query(connection, "select * from user", 
    				new BeanListHandler<User>(User.class));
    		for (User user : users) {
    			System.out.println(user);
    		}
    		DbUtils.close(connection);
    	}

    写一个参数文件dbcp.properties:

    //创建输入流对象,来读取配置文件dbcp.properties

    7、druid的使用

    介绍:它除了提供性能卓越的连接池功能外,还集成了SQL监控,黑名单拦截等功能,用它自己的话说,Druid是“为监控而生”。借助于阿里这个平台的号召力,产品一经发布就赢得了大批用户的拥趸,从用户使用的反馈来看,Druid也确实没让用户失望。

    使用步骤:

    导包   

    书写配置文件druid.properties    

    书写druid工具类,来生成并提供druid的数据源   

    /**
     * 此类专门用于生成druid数据源
     * @author Administrator
     *
     */
    public class DruidUtils {
    	//声明数据源
    	private static DataSource dataSource = null;
    	
    	//为数据源赋值
    	static {
    		Properties properties = new Properties();
    		FileInputStream fStream = null;
    		try {
    			fStream = new FileInputStream("src/druid.properties");
    			properties.load(fStream);
    			dataSource = DruidDataSourceFactory.createDataSource(properties);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} finally {
    			try {
    				if (fStream != null) {
    					fStream.close();
    				}
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    	
    	//返回获取数据源的方法
    	public static DataSource getDataSource() {
    		return dataSource;
    	}
    }

    书写测试代码

    public static void main(String[] args) {
    		//使用druid+dbutils完成添加操作
    		QueryRunner qRunner = new QueryRunner(DruidUtils.getDataSource());
    		try {
    			int row = qRunner.update("insert into user values (null, ?, ?, ?)", 
    								"小阔爱", "666", "男");
    			System.out.println(row > 0 ? "添加成功" : "添加失败");
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}

    8、c3p0的使用

    介绍:C3P0是我使用的第一款数据库连接池,在很长一段时间内,它一直是Java领域内数据库连接池的代名词,当年盛极一时的Hibernate都将其作为内置的数据库连接池,可以业内对它的稳定性还是认可的。C3P0功能简单易用,稳定性好这是它的优点,但是性能上的缺点却让它彻底被打入冷宫。C3P0的性能很差,差到即便是同时代的产品相比它也是垫底的,更不用和Druid、HikariCP等相比了。正常来讲,有问题很正常,改就是了,但c3p0最致命的问题就是架构设计过于复杂,让重构变成了一项不可能完成的任务。随着国内互联网大潮的涌起,性能有硬伤的c3p0彻底的退出了历史舞台。

    使用步骤:

    导包   

    书写c3p0.properties文件,一定要放在src下   

    如果c3p0的配置文件名称叫c3p0.properties,并且放在了src下,那么就会自动读取配置文件!

    测试代码  

    测试代码:
    public class C3P0Demo {
    	private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
    	
    	public static void main(String[] args) {
    		QueryRunner qRunner = new QueryRunner(dataSource);
    		try {
    			User user = qRunner.query("select * from user where uid = ?",
    					new BeanHandler<User>(User.class), 2);
    			System.out.println(user);
    		} catch (SQLException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();

    五,duid,dbcp,c3p0 的区别。

     

     

     

     

     

     

     

    展开全文
  • 自定义数据库连接

    千次阅读 2019-04-20 13:21:41
    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。 应用程序直接获取连接的缺点: 用户每次请求都需要向数据库获得链接,而数据库创建连接...

    时间很快就到周末了,学习计划也已经进行了五天了,既然是周末的话,那当然要多学习一点知识,毕竟拥有这么充裕的时间。

    今天的学习内容是数据库连接池。
    那什么是数据库连接池,它有什么作用是我们首先会想到的问题。
    数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。

    应用程序直接获取连接的缺点:
    用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。

    缺点显而易见,应用程序在创建连接和销毁连接的时候是极其消耗资源的,而使用数据库连接池则能够优化程序性能。

    连接池原理:
    在服务器端一次性地创建多个连接,将多个连接保存在一个连接池对象中,当请求需要操作数据库时,不会为请求创建新的连接,而是直接从连接池中获得一个连接。当操作数据库结束,并不需要真正的去关闭连接,而是将连接放回到连接池中。

    了解了数据库连接池的优点后,我们关心的是该如何去实现数据库连接池呢?
    在Java中提供了javax.sql.Datasource接口用于实现数据库连接池。
    老话说得好,光说不练假把式,只练不打无用功,现在我们就来写一个程序感受一下。

    在MyEclipse中新建一个web项目,取名demo。
    新建一个类,取名MyDataSource,然后实现DataSource接口,要实现的方法非常多,但是不用紧张,我们只关注两个方法。

    public Connection getConnection() throws SQLException {
    	return null;
    }
    
    public Connection getConnection(String username, String password)
    		throws SQLException {
    	return null;
    }
    

    方便起见,我们只看无参的getConnection()方法。自定义的连接池需要有如下功能

    • 一次性地创建多个连接
    • 实现getConnection方法,从连接池获得一个连接
    • 当用户使用连接后,提供方法将连接放回到连接池中
      代码如下:
    /**
     * 自定义连接池
     * 
     *  一次性地创建多个连接
     *  
     *  实现getConnection方法,从连接池获得一个连接
     *  
     *  当用户使用连接后,提供方法将连接放回到连接池中
     *  
     * @author Administrator
     * 
     */
    public class MyDataSource implements DataSource {
    	
    	private LinkedList<Connection> dataSources = new LinkedList<Connection>();
    	
    	public MyDataSource(){
    		//一次性创建10个连接
    		for(int i = 0;i < 10;i++){
    			try {
    				Connection connection = JDBCUtils.getConnection();
    				//将连接加入到连接池中
    				dataSources.add(connection);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			
    		}
    	}
    
    	public PrintWriter getLogWriter() throws SQLException {
    		return null;
    	}
    
    	public void setLogWriter(PrintWriter out) throws SQLException {
    
    	}
    
    	public void setLoginTimeout(int seconds) throws SQLException {
    
    	}
    
    	public int getLoginTimeout() throws SQLException {
    		return 0;
    	}
    
    	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    		return null;
    	}
    
    	public <T> T unwrap(Class<T> iface) throws SQLException {
    		return null;
    	}
    
    	public boolean isWrapperFor(Class<?> iface) throws SQLException {
    		return false;
    	}
    
    	public Connection getConnection() throws SQLException {
    		//取出连接池中的一个连接
    		Connection connection = dataSources.removeFirst();//删除第一个连接并返回
    		return connection;
    	}
    
    	public Connection getConnection(String username, String password)
    			throws SQLException {
    		return null;
    	}
    }
    

    代码中的JDBCUtils是我编写的一个工具类,在之前的博客中也都有提及,为了方便大家,我就再贴一次。

    /**
     * JDBC 工具类,抽取公共方法
     * 
     * @author seawind
     * 
     */
    public class JDBCUtils {
    	private static final String DRIVERCLASS;
    	private static final String URL;
    	private static final String USER;
    	private static final String PWD;
    
    	static {
    		ResourceBundle bundle = ResourceBundle.getBundle("dbconfig");
    		DRIVERCLASS = bundle.getString("DRIVERCLASS");
    		URL = bundle.getString("URL");
    		USER = bundle.getString("USER");
    		PWD = bundle.getString("PWD");
    	}
    
    	// 建立连接
    	public static Connection getConnection() throws Exception {
    		loadDriver();
    		return DriverManager.getConnection(URL, USER, PWD);
    	}
    
    	// 装载驱动
    	private static void loadDriver() throws ClassNotFoundException {
    		Class.forName(DRIVERCLASS);
    	}
    
    	// 释放资源
    	public static void release(ResultSet rs, Statement stmt, Connection conn) {
    		if (rs != null) {
    			try {
    				rs.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			rs = null;
    		}
    
    		release(stmt, conn);
    	}
    
    	public static void release(Statement stmt, Connection conn) {
    		if (stmt != null) {
    			try {
    				stmt.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			stmt = null;
    		}
    		if (conn != null) {
    			try {
    				conn.close();
    			} catch (SQLException e) {
    				e.printStackTrace();
    			}
    			conn = null;
    		}
    	}
    }
    

    现在新建一个测试类,取名MyDataSourceTest

    public class MyDataSourceTest {
    	public static void main(String[] args) throws SQLException {
    		//创建连接池
    		MyDataSource dataSource = new MyDataSource();
    		//从连接池中获得一个连接
    		Connection connection = dataSource.getConnection();
    		//操作数据库
    		String sql = "select * from account";
    		PreparedStatement stmt = connection.prepareStatement(sql);
    		ResultSet rs = stmt.executeQuery();
    		while (rs.next()) {
    			System.out.println(rs.getString("name"));
    		}
    		JDBCUtils.release(stmt, connection);
    	}
    }
    

    运行测试类
    在这里插入图片描述
    控制台成功输出用户姓名。
    但是这段程序是有问题的,因为JDBCUtils工具类中的release()方法会将连接关闭,而我们的想法是将连接归还到连接池而不是关闭它。实现方法有很多种,你可以在自定义连接池MyDataSource中添加一个方法用于归还连接。
    重新修改MyDataSource类,代码如下

    /**
     * 自定义连接池
     * 
     *  一次性地创建多个连接
     *  
     *  实现getConnection方法,从连接池获得一个连接
     *  
     *  当用户使用连接后,提供方法将连接放回到连接池中
     *  
     * @author Administrator
     * 
     */
    public class MyDataSource implements DataSource {
    	
    	private LinkedList<Connection> dataSources = new LinkedList<Connection>();
    	
    	public MyDataSource(){
    		//一次性创建10个连接
    		for(int i = 0;i < 10;i++){
    			try {
    				Connection connection = JDBCUtils.getConnection();
    				//将连接加入到连接池中
    				dataSources.add(connection);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			
    		}
    	}
    
    	public PrintWriter getLogWriter() throws SQLException {
    		return null;
    	}
    
    	public void setLogWriter(PrintWriter out) throws SQLException {
    
    	}
    
    	public void setLoginTimeout(int seconds) throws SQLException {
    
    	}
    
    	public int getLoginTimeout() throws SQLException {
    		return 0;
    	}
    
    	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    		return null;
    	}
    
    	public <T> T unwrap(Class<T> iface) throws SQLException {
    		return null;
    	}
    
    	public boolean isWrapperFor(Class<?> iface) throws SQLException {
    		return false;
    	}
    	
    	/**
    	 * 添加一个方法,用于归还连接
    	 * @param conn
    	 */
    	public void releaseConnection(Connection conn){
    		dataSources.add(conn);
    		System.out.println("将连放回到连接池中,数量" + dataSources.size());
    	}
    
    	public Connection getConnection() throws SQLException {
    		//取出连接池中的一个连接
    		Connection connection = dataSources.removeFirst();//删除第一个连接并返回
    		System.out.println("取出一个连接,剩余" + dataSources.size() + "个连接");
    		return connection;
    	}
    
    	public Connection getConnection(String username, String password)
    			throws SQLException {
    		return null;
    	}
    }
    
    

    测试类中最后就应该调用你刚刚添加的方法,运行测试类
    在这里插入图片描述
    结果很明显,目的达到了。
    这样就实现了一个简易的数据库连接池,但该程序其实有一个很不好的地方,因为如果想要将连接放回连接池而不是关闭它,就得调用在MyDataSource类中自己添加的方法,而很多人或许已经习惯了JDBC的编程,会很习惯地去调用close()方法,而一旦调用了close()方法,连接就被关闭了,不会放回连接池了。所以你就需要通知用户去主动使用你添加的API,这种方法其实是不可取的。那有什么办法能够让调用者随着自己的编程习惯去调用close()方法的同时,还能够将连接放回连接池而不是关闭它呢?
    我们可以去修改close()方法原来的逻辑,怎么修改呢?
    在Java中有三种方法可以增强原有的方法

    • 类继承 、方法覆盖
      必须控制对象创建,才能使用该方式

    • 装饰者模式方法加强
      必须和目标对象实现相同接口或继续相同父类,特殊构造器(传入被包装对象)

    • 动态代理

    有关方法增强的问题,可以参考我的这篇博客。
    理解Java方法增强
    熟悉方法增强的小伙伴们可以忽略哈。(手动滑稽)

    我们使用动态代理来对Connection接口的run()方法进行增强。
    修改MyDataSource类中的代码

    /**
     * 自定义连接池
     * 
     * 一次性地创建多个连接
     * 
     * 实现getConnection方法,从连接池获得一个连接
     * 
     * 当用户使用连接后,提供方法将连接放回到连接池中
     * 
     * @author Administrator
     * 
     */
    public class MyDataSource implements DataSource {
    
    	private LinkedList<Connection> dataSources = new LinkedList<Connection>();
    
    	public MyDataSource() {
    		// 一次性创建10个连接
    		for (int i = 0; i < 10; i++) {
    			try {
    				Connection connection = JDBCUtils.getConnection();
    				// 将连接加入到连接池中
    				dataSources.add(connection);
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    
    		}
    	}
    
    	public PrintWriter getLogWriter() throws SQLException {
    		return null;
    	}
    
    	public void setLogWriter(PrintWriter out) throws SQLException {
    
    	}
    
    	public void setLoginTimeout(int seconds) throws SQLException {
    
    	}
    
    	public int getLoginTimeout() throws SQLException {
    		return 0;
    	}
    
    	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
    		return null;
    	}
    
    	public <T> T unwrap(Class<T> iface) throws SQLException {
    		return null;
    	}
    
    	public boolean isWrapperFor(Class<?> iface) throws SQLException {
    		return false;
    	}
    
    	/**
    	 * 添加一个方法,用于归还连接
    	 * 
    	 * @param conn
    	 */
    	public void releaseConnection(Connection conn) {
    		dataSources.add(conn);
    		System.out.println("将连放回到连接池中,数量" + dataSources.size());
    	}
    
    	public Connection getConnection() throws SQLException {
    		// 取出连接池中的一个连接
    		final Connection connection = dataSources.removeFirst();// 删除第一个连接并返回
    		System.out.println("取出一个连接,剩余" + dataSources.size() + "个连接");
    		// 将目标connection对象进行增强
    		Connection connProxy = (Connection) Proxy.newProxyInstance(connection
    				.getClass().getClassLoader(), connection.getClass()
    				.getInterfaces(), new InvocationHandler() {
    
    			//执行代理对象的任何方法都将执行invoke方法
    			public Object invoke(Object proxy, Method method, Object[] args)
    					throws Throwable {
    				//只需要增强close方法
    				if(method.getName().equals("close")){
    					//需要增强的方法
    					//不将连接真正关闭,将连接放回连接池
    					releaseConnection(connection);
    					return null;
    				}else{
    					//不需要增强的方法,保持方法原有的功能即可
    					return method.invoke(connection, args);
    				}
    			}
    		});
    		return connProxy;
    	}
    
    	public Connection getConnection(String username, String password)
    			throws SQLException {
    		return null;
    	}
    }
    
    

    回到测试类,此时调用Connection接口中的close()方法,然后执行
    在这里插入图片描述
    方法成功增强了。调用者在调用close()方法后并不会把连接关闭了,而是放回连接池中。由此我们的想法便实现了。

    展开全文
  • 数据库连接大全

    万次阅读 热门讨论 2009-10-27 14:56:00
    -----------MySQL 连接方式-----------MySQL Connector/ODBC 2.50 (MyODBC 2.50)连接方式本地数据库连接Driver={mySQL};Server=localhost;Option=16834;Database=myDataBase; 远程数据连接:Driver={mySQL};Server=...
  • GIS创建数据库

    千次阅读 2020-11-02 19:29:14
    实验目的:了解数据库创建的基本流程涉及到的操作 在ArcCatalog中进行 1·创建新的空地理数据库 坐标可以导入一个现有的。 2·创建新的要素数据集,设置信息city1 要素类(feature class)是指具有相同的几何特征的...
  • Java程序数据库连接,数据源配置,数据库连接池 Java程序,用到的数据库一定要配置数据源吗? 一般写小程序直接在程序里设置连接就可以了,而大的系统一般要配置数据源 数据源是要配置到中间件服务器中的...
  • 数据库实验一:创建数据库和表

    千次阅读 2019-10-20 09:19:27
    数据实验一:创建数据库和表 1.1 实验目的 掌握MySQL下创建、修改、删除数据库和表的方法; 掌握查看数据库及其构成、数据库文件、数据库引擎、数据库对象、字符集、校对规则等应用; 掌握数据库...
  • JDBC 数据库连接技术简介

    千次阅读 多人点赞 2019-10-29 13:32:21
    介绍数据库连接技术 JDBC。
  • 自己动手写数据库连接

    千次阅读 2016-09-13 23:45:42
    本篇会实现一个数据库连接池,目的是让大家对数据库连接池的实现方式有一个深入的认识。 为什么要使用连接池? 使用数据库连接池可以节省系统的许多的开销,频繁的操作数据库是一个很耗费资源的行为,网站的响应的...
  • SQL Server 2012 创建数据库

    万次阅读 多人点赞 2019-04-15 00:06:01
    文章目录基本概念使用SSMS工具创建使用SQL语言创建 基本概念 主要数据文件(.mdf)     主要数据文件包含数据库的启动信息,并指向数据库中的其他文件,用户数据和对象可存储在此文件中,一个数据库只有一个...
  • 数据库连接池探究

    千次阅读 2012-05-18 17:09:49
    1.简介  数据库连接是一种关键的...数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放
  • JDBC数据库连接基础知识

    千次阅读 多人点赞 2019-03-17 00:00:26
    JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和...
  • 1、首先我们先在IDEA上创建一个SpringBoot的Web项目 (1)file ——&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; new ——&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt; project (2)——&amp;amp;...
  • golang 数据库 连接连接

    千次阅读 2020-01-13 10:11:26
    database/sql database/sql是golang的标准库之一,它提供了一系列接口方法,用于访问关系数据库。它并不会提供数据库特有的方法,那些特有的方法交给...和其他语言不一样,它并是数据库连接。golang中的连接来...
  • 文章目录Golang 侧数据库连接池原理和参数调优数据库连接数据库连接池的设计Go 的数据库连接池Go 数据库连接池的设计建立连接释放连接清理连接配置 sql.DB 以获得更好的性能maxOpenmaxIdlemaxLifeTime在实践和压测...
  • Python实现Mysql数据库连接

    万次阅读 2016-05-07 10:57:20
    python连接Mysql数据库: python编程中可以使用MySQLdb进行数据库的连接及诸如查询/插入/更新等操作,但是每次连接mysql数据库请求时,都是独立的去请求访问,相当浪费资源,而且...python的数据库连接池包 DBUtils:
  • Java中数据库连接池简单介绍

    千次阅读 2017-12-30 20:35:47
    提纲: 1.... 3.tomcat管理连接池.DataSource(数据源/数据库连接池 )简单介绍连接池:创建多个Connection对象,放入到连接池(其实就是一个容器)中 当使用时,从连接池中取出一个连接对象,使用完成
  • Java程序数据库连接,数据源配置,数据库连接池Java程序,用到的数据库一定要配置数据源吗?一般写小程序直接在程序里设置连接就可以了,而大的系统一般要配置数据源数据源是要配置到中间件服务器中的(比如:Tomcat,...
  • 【mysql调优】数据库连接池配置优化

    千次阅读 2020-03-27 23:02:23
    创建数据库连接是一个很耗时的操作,也容易对数据库造成安全隐患。所以,在程序初始化的时候,集中创建多个数据库连接,并把他们集中管理,供程序使用,可以保证较快的数据库读写速度,还更加安全可靠 官方解释: ...
  • java数据库连接

    千次阅读 2007-12-05 10:01:00
    java数据库连接 JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以...
  • 数据库连接池(JSP)

    千次阅读 2013-12-03 21:52:06
    一、为什么使用数据库连接池   用户每次请求都需要向数据库获得连接,而数据库创建连接通常要消耗相对较大的资源,创建时间也长。假设网站一天10万的访问量,数据库服务器就需要创建10万次连接,极大浪费数据库的...
  • 数据库连接技术整理

    千次阅读 2017-02-09 15:40:14
    ODBC(Open Database Connectivity,开放数据库互连)是微软公司开放服务结构(WOSA,Windows Open Services Architecture)中有关数据库的一个组成部分,它建立了一组规范,并提供了一组对数据库访问的标准API...
  • Spring是如何进行数据库连接

    万次阅读 2018-12-22 18:51:33
    Table of Contents 概述 java原生JDBC JdbcTemplate jdbcTemplate查询query jdbcTemplate更新操作update/delete... ...数据库连接和DataSource 总结 本篇博客基于java8. 概述 Spring框架提...
  • Java中JDBC的数据库连接

    万次阅读 2016-07-19 06:09:30
    数据库连接池 池参数(所有池参数都有默认值): 初始大小:10个 最小空闲连接数:3个 增量:一次创建的最小单位(5个) 最大空闲连接数:12个 最大连接数:20个 最大的等待时间:1000毫秒 四大连接参数...
  • JavaWeb:JDBC之数据库连接

    千次阅读 2016-10-31 12:28:20
    JavaWeb:JDBC之数据库连接池 池参数(所有池参数都有默认值) 初始大小:10个 最小空闲连接数:3个 增量:一次创建的最小单位(5个) 最大空闲连接数:12个 最大连接数:20个 最大的等待时间:1000毫秒 四大连接...
  • 浅析数据库连接池(二)

    千次阅读 2015-06-11 17:21:53
    上一篇博客,主要是简单的介绍了普通数据库连接的过程以及耗费的资源,并且简单的谈了下连接池,这篇我们主要来看看数据库连接池的使用以及它最优的配置。 总目录: -1.数据库连接过程是怎样的? -2.连接所占用的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 259,356
精华内容 103,742
关键字:

创建数据库连接的目的是