精华内容
下载资源
问答
  • 昨天因为要使用PowerDesigner将数据库视图导出,但是在配置Configuration Connect 时出现了问题,在我添加数据源的时候,一直出错,网上一查发现PowerDesigner不支持64位数据源,于是我在配置32位数据源的时候一直...

    昨天因为要使用PowerDesigner将数据库视图导出,但是在配置Configuration Connect 时出现了问题,在我添加数据源的时候,一直出错,网上一查发现PowerDesigner不支持64位数据源,于是我在配置32位数据源的时候一直发生如图下的这个问题

     

    因为是选择的是如图中的这个Microsoft ODBC for Oracle 选项,网上说要我下载客户端,无语了,我怎么有这么多时间去搞这些花里胡哨的东西。有很多方法可以让我去选择,但是我觉得最方便的就是下载下面的者两个驱动包

    base包:instantclient-basic-windows.x64-11.2.0.3.0.zip

    ODBC包:instantclient-odbc-windows.x64-11.2.0.3.0.zip</

    展开全文
  • 主从数据源切换(两个版本)一是利用Spring提供的AbstractRoutingDataSource,二是使用自定义数据源切换类 模块 actdemo ├── mybatisGenerator --mybatis生成程序 └── web --SpringBoot项目 集成activiti6.0.0...
  • 动态添加ODBC数据源的两种方法

    千次阅读 2014-11-20 19:34:21
    在使用 VC、 VB、 Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置 ODBC数据源。对于一般用户而言,配置 ODBC数据源可能是一件比较困难的工作。而且,在实际应用中,用户往往要求在同一个...
    在使用 VC、 VB、 Delphi等高级语言编写数据库应用程序时,往往需要用户自己在控制面板中配置 ODBC数据源。对于一般用户而言,配置 ODBC数据源可能是一件比较困难的工作。而且,在实际应用中,用户往往要求在同一个应用程序中访问不同的数据源,因此采用一般的加载方法就有了无法克服的缺陷。为能在程序中完成这一工作,方便应用程序的使用,本文以 VC为开发环境介绍两种在应用程序中动态加载 ODBC系统数据源的方法。 
    
    方法一:修改注册表  
    设计思路  
    一般情况下,当用户在控制面板中配置好 ODBC数据源后, Windows系统便在注册表中加入了一些子键来存储用户的配置结果。当应用程序需要用到数据源时, Windows便会通知底层接口查阅注册表中该数据源的配置。如果用户删除了某个 ODBC数据源,那么也会在注册表中有所反应。如果配置的数据源是用户数据源, Windows系统便会修改注册表的 HKEY_CURRENT_USER\SOFTWARE\ODBC\ODBC.INI子键;如果配置的数据源是系统数据源, Windows系统便会修改注册表的 HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI主键。因此,我们可以在应用程序中使用 Windows API中的注册表编辑函数来完成 Windows所做的工作,这样就可以达到动态加载数据源的目的。 
    具体实现 
    对于不同类型的数据源,注册表的修改也各有不同,但基本上都要修改两个地方。一个是在 ODBC.INI子键下建立一个与数据源描述名同名的子键,并在该子键下建立与数据源配置相关的项;另一个是在 \ODBC.INI\ODBC Data Sources子键下建立一个新项以便告诉驱动程序管理器 ODBC数据源的类型。下面以配置一个 Microsoft Access数据源为例给出实现此功能的函数的代码。  


    #include <Windows.h>
    #include <atlstr.h>


    /* 
    此函数可以动态创建Microsoft Access数据库的系统DSN。如果要创建用户DSN只需要把HKEY_LOCAL_MACHINE改为HKEY_CURRENT_USER
    如果之前已经存在了一个相同名称的DSN,则删除之前的再创建
    strSourceName是要创建的数据源名,也就是odbc中的DSN名称
    strSourceDb是数据库存放路径(如D:\Access.mdb)
    strDescription是数据源的描述字符串(仅仅是个字符串对数据源的描述)
    cstrUid 访问数据源的uid
    cstrPwd访问数据源的pwd
    返回TRUE为创建DSN成功
    */  
    BOOL CreateAccessDSN(CString cstrSourceName,CString cstrSourceDb, CString cstrDescription,CString cstrUid,CString cstrPwd)  
    {  
    //存放打开的注册表键  
    HKEY hKey;  
    DWORD dw;  
    //存放注册表 API函数执行的返回值  
    LONG lReturn;  
    //存放要打开的子键  
    CString cstrSubKey;  
    //检测是否安装了 MS Access ODBC driver:odbcjt32.dll  
    //获得 Windows系统目录  
    TCHAR sysDir[MAX_PATH];  
    TCHAR drvName[]=_T("\\odbcjt32.dll");  
    ::GetSystemDirectory (sysDir,MAX_PATH);  
    lstrcat(sysDir,drvName);  
    /*CFileFind findFile;  
    if(!findFile.FindFile (sysDir))  
    {  
    AfxMessageBox("您的计算机系统中没有安装 MS ssAcce的 ODBC驱动程序 odbcjt32.dll,您将无法加载该类数据源。" ,MB_OK|MB_ICONSTOP);  
    return false;  
    }  */
    WIN32_FIND_DATA FindFileData;  
    HANDLE hFind = FindFirstFile(sysDir, &FindFileData);  
    if(hFind == INVALID_HANDLE_VALUE)
    {
    return false;
    }
    cstrSubKey=_T("SOFTWARE\\ODBC\\ODBC.INI\\")+cstrSourceName;  


    //先判断要创建的ODBC数据源是否存在
    lReturn=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,KEY_READ,&hKey);
    if (lReturn==ERROR_SUCCESS)//打开成功说明已经存在
    {
    //删除它和他的所有子键
    BOOL bRet=RegDelnode(hKey,(LPTSTR)cstrSubKey.GetString());
    if (!bRet)//删除失败
    return FALSE;
    }


    //创建 ODBC数据源在注册表中的子键  
    lReturn=::RegCreateKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dw);  
    if(lReturn != ERROR_SUCCESS)  
    return false; 
    //设置数据源的各项参数  
    CString cstrDbq=cstrSourceDb;  
    CString cstrDriver=sysDir;  
    DWORD dwDriverId=25;  
    CString cstrFil=_T("MS Access") ;  
    CString cstrPWD=cstrPwd;  
    DWORD dwSafeTransactions=0;  
    CString cstrUID=cstrUid;  
    ::RegSetValueEx (hKey,_T("DBQ") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrDbq),cstrDbq .GetLength ()) ;
    ::RegSetValueEx (hKey,_T("Description") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDescription),cstrDescription.GetLength());  
    ::RegSetValueEx (hKey,_T("Driver"),0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDriver),cstrDriver .GetLength ());  
    ::RegSetValueEx (hKey,_T("DriverId") ,0L,REG_DWORD,(CONST BYTE*)(&dwDriverId),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("FIL") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrFil),cstrFil .GetLength ()); 
    ::RegSetValueEx (hKey,_T("PWD") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrPWD),cstrPWD.GetLength ()) ; 
    ::RegSetValueEx (hKey,_T("SafeTransactions") ,0L,REG_DWORD,(CONST BYTE*)(&dwSafeTransactions),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("UID") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrUID),cstrUID .GetLength ()); 
    ::RegCloseKey(hKey);  
    //创建 ODBC数据源的 Jet子键  
    cstrSubKey+=_T("\\Engines\\Jet") ;  
    lReturn=::RegCreateKeyEx (HKEY_LOCAL_MACHINE ,(LPCTSTR)cstrSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dw);  
    if(lReturn != ERROR_SUCCESS)  
    return false;  
    //设置该子键下的各项参数  
    CString strImplict=_T(" ") ;  
    CString strUserCommit=_T("Yes") ;  
    DWORD dwPageTimeout=5;  
    DWORD dwThreads=3;  
    DWORD dwMaxBufferSize=2048;  
    ::RegSetValueEx (hKey,_T("ImplictCommitSync") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)strImplict),strImplict.GetLength ()+1);  
    ::RegSetValueEx (hKey,_T("MaxBufferSize") ,0L,REG_DWORD,(CONST BYTE*)(&dwMaxBufferSize),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("PageTimeout") ,0L,REG_DWORD,(CONST BYTE*)(&dwPageTimeout),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("Threads") ,0L,REG_DWORD,(CONST BYTE*)(&dwThreads),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("UserCommitSync") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)strUserCommit),strUserCommit.GetLength ());  
    ::RegCloseKey (hKey);  
    //设置 ODBC数据库引擎名称  
    lReturn=::RegOpenKeyEx (HKEY_LOCAL_MACHINE ,_T("SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources") ,0L,KEY_WRITE,&hKey);  
    if(lReturn !=ERROR_SUCCESS)  
    return false;  
    CString strDbType=_T("Microsoft Access Driver (*.mdb)"); 
    ::RegSetValueEx (hKey,cstrSourceName,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)strDbType),strDbType.GetLength ());  
    return true;  
    }  


    由于在动态加载中,一般只会改变数据库文件、数据源说明以及数据源描述,故上述函数可以实现应用中的大部分要求。如果应用中还需要作更多的改变,那么也可以通过改变函数参数的方式加以实现。对于需要动态加载多种类型数据源的情况,可以用具有不同参数的重载函数去实现。


    方法二:利用 DLL  
    设计思路  
    创建ODBC数据源可以调用Windows系统子目录下的动态链接库ODBCCP32.DLL中的函数SQLConfigDataSource(),该函数可以动态地增加、修改和删除数据源。SQLConfigDataSource()函数的原型如下:  
    BOOL SQLConfigDataSource(HWND hwndParent,WORD fRequest, LPCSTR lpszDriver, LPCSTR lpszAttributes);  
     hwndParent参数是父窗口句柄。如果该值为 NULL,将不会显示与父窗口有关的对话框。 如果参数 IpszAttributes提供的信息不够完善,在创建过程中就会出现对话框要求用户提供相应信息。 
    fRequest参数可以设置为下面的数值之一:  
     ODBC_ADD_DSN:增加一个新的用户数据源;  
     ODBC_CONFIG_DSN:修改(配置)一个已经存在的用户数据源;  
     ODBC_REMOVE_DSN:删除一个已经存在的用户数据源;  
     ODBC_ADD_SYS_DSN:增加一个新的系统数据源;  
     ODBC_CONFIG_SYS_DSN:修改 (配置 )一个已经存在的系统数据源;  
     ODBC_REMOVE_SYS_DSN:删除一个已经存在的系统数据源。 
       ODBC_REMOVE_DEFAULT_DSN:删除省缺的数据源说明部分。 
    lpszDriver参数用于传递数据库引擎的名字,等同于方法一中 strDbType变量。再比如要加载的是Excel数据库,那么数据库引擎名称就为Microsoft Excel Driver(*.xls)
    lpszAttributes参数为一连串的"KeyName=value"字符串,每两个KeyName值之间用"\0"字符隔开(或者\0隔开即可)。如 DSN=Personnel Data\0UID=Smith\0DATABASE=Personnel。KeyName主要是新数据源缺省的驱动程序注册说明,其中最主要的关键字是"DSN"(新数据源的名称)和"DBQ"(数据源的地址),其余关键字则根据不同的数据源有不同要求。关于该参数的详细设置请参阅 MSDN中 SQLConfigDataSource()函数的帮助文档和各种 ODBC驱动程序文档。 http://msdn.microsoft.com/zh-cn/library/ms130822.aspx 
    具体实现  
    由于 VC的缺省库文件中不包含 SQLConfigDataSource()函数,因此使用该函数之前需要包涵头文件#include <odbcinst.h>,在工程的 Settings属性对话框 Link属性页的 Object/library modules编辑框中增加 odbc32.lib(或者用#pragma comment(lib, "ODBCCP32.lib")方式链接),同时保证系统目录 system32下有文件 odbccp32.dll。


    仍以 Microsoft Access为例,设置数据源名为 demo,数据源描述为 "示例数据源 ",那么在需要动态加载数据源的地方加入下列代码即可:  
     ::SQLConfigDataSource (NULL,ODBC_ADD_SYS_DSN," Microsoft Access Driver (*.mdb)"," DSN=demo\0Descirption=示例数据库 " );  
    或者
    SQLConfigDataSource(NULL,ODBC_ADD_DSN,"Microsoft Access Driver (*.mdb)",   
    "DSN=Personnel\0"   
    "DBQ=C:\\My Documents\\dq.mdb\0"   
    "Description=ODBC数据源\0"   
    "DataDirectory=C:\\My Documents\0"   
    "\0");




    小结 

    上述两种方法都可以实现动态加载各种类型的 ODBC数据源,并且在 Windows95/98/NT/2000环境下调试通过。方法一在实现时需要较多的代码,方法二所需代码虽少,但需要额外文件的支持,而且随着数据源配置的灵活性的增加,为了形成 lpszAttributes字符串,其代码长度也会相应增加。由于从控制面板配置数据源使得程序员可以获得更加直观的理解,所以对于注册表中各项值以及相应项名称的获得除了可以查阅相关驱动程序的文档外,程序员也可以在编程前先通过控制面板配置 ODBC数据源,然后根据注册表中相应部分的内容进行编程。 



    // addsqlite.cpp : 定义控制台应用程序的入口点。
    //


    #include "stdafx.h"
    #include <Windows.h>
    #include <odbcinst.h>
    #pragma comment(lib, "ODBCCP32.lib")


    #include <atlstr.h>


    #include <windows.h>
    #include <stdio.h>
    #include <strsafe.h>




    //*************************************************************
    //
    //  RegDelnodeRecurse()
    //
    //  Purpose:    Deletes a registry key and all its subkeys / values.
    //
    //  Parameters: hKeyRoot    -   Root key
    //              lpSubKey    -   SubKey to delete
    //
    //  Return:     TRUE if successful.
    //              FALSE if an error occurs.
    //
    //*************************************************************


    BOOL RegDelnodeRecurse (HKEY hKeyRoot, LPTSTR lpSubKey)
    {
    LPTSTR lpEnd;
    LONG lResult;
    DWORD dwSize;
    TCHAR szName[MAX_PATH];
    HKEY hKey;
    FILETIME ftWrite;


    // First, see if we can delete the key without having
    // to recurse.


    lResult = RegDeleteKey(hKeyRoot, lpSubKey);


    if (lResult == ERROR_SUCCESS) 
    return TRUE;


    lResult = RegOpenKeyEx (hKeyRoot, lpSubKey, 0, KEY_READ, &hKey);


    if (lResult != ERROR_SUCCESS) 
    {
    if (lResult == ERROR_FILE_NOT_FOUND) {
    printf("Key not found.\n");
    return TRUE;

    else {
    printf("Error opening key.\n");
    return FALSE;
    }
    }


    // Check for an ending slash and add one if it is missing.


    lpEnd = lpSubKey + lstrlen(lpSubKey);


    if (*(lpEnd - 1) != TEXT('\\')) 
    {
    *lpEnd =  TEXT('\\');
    lpEnd++;
    *lpEnd =  TEXT('\0');
    }


    // Enumerate the keys


    dwSize = MAX_PATH;
    lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
    NULL, NULL, &ftWrite);


    if (lResult == ERROR_SUCCESS) 
    {
    do {


    StringCchCopy (lpEnd, MAX_PATH*2, szName);


    if (!RegDelnodeRecurse(hKeyRoot, lpSubKey)) {
    break;
    }


    dwSize = MAX_PATH;


    lResult = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL,
    NULL, NULL, &ftWrite);


    } while (lResult == ERROR_SUCCESS);
    }


    lpEnd--;
    *lpEnd = TEXT('\0');


    RegCloseKey (hKey);


    // Try again to delete the key.


    lResult = RegDeleteKey(hKeyRoot, lpSubKey);


    if (lResult == ERROR_SUCCESS) 
    return TRUE;


    return FALSE;
    }


    //*************************************************************
    //
    //  RegDelnode()
    //
    //  Purpose:    Deletes a registry key and all its subkeys / values.
    //
    //  Parameters: hKeyRoot    -   Root key
    //              lpSubKey    -   SubKey to delete
    //
    //  Return:     TRUE if successful.
    //              FALSE if an error occurs.
    //
    //*************************************************************


    BOOL RegDelnode (HKEY hKeyRoot, LPTSTR lpSubKey)
    {
    TCHAR szDelKey[MAX_PATH*2];


    StringCchCopy (szDelKey, MAX_PATH*2, lpSubKey);
    return RegDelnodeRecurse(hKeyRoot, szDelKey);


    }




    /* 
    此函数可以动态创建Microsoft Access数据库的系统DSN。如果要创建用户DSN只需要把HKEY_LOCAL_MACHINE改为HKEY_CURRENT_USER
    如果之前已经存在了一个相同名称的DSN,则删除之前的再创建
    strSourceName是要创建的数据源名,也就是odbc中的DSN名称
    strSourceDb是数据库存放路径(如D:\Access.mdb)
    strDescription是数据源的描述字符串(仅仅是个字符串对数据源的描述)
    cstrUid 访问数据源的uid
    cstrPwd访问数据源的pwd
    返回TRUE为创建DSN成功
    */  
    BOOL CreateAccessDSN(CString cstrSourceName,CString cstrSourceDb, CString cstrDescription,CString cstrUid,CString cstrPwd)  
    {  
    //存放打开的注册表键  
    HKEY hKey;  
    DWORD dw;  
    //存放注册表 API函数执行的返回值  
    LONG lReturn;  
    //存放要打开的子键  
    CString cstrSubKey;  
    //检测是否安装了 MS Access ODBC driver:odbcjt32.dll  
    //获得 Windows系统目录  
    TCHAR sysDir[MAX_PATH];  
    TCHAR drvName[]=_T("\\odbcjt32.dll");  
    ::GetSystemDirectory (sysDir,MAX_PATH);  
    lstrcat(sysDir,drvName);  
    /*CFileFind findFile;  
    if(!findFile.FindFile (sysDir))  
    {  
    AfxMessageBox("您的计算机系统中没有安装 MS ssAcce的 ODBC驱动程序 odbcjt32.dll,您将无法加载该类数据源。" ,MB_OK|MB_ICONSTOP);  
    return false;  
    }  */
    WIN32_FIND_DATA FindFileData;  
    HANDLE hFind = FindFirstFile(sysDir, &FindFileData);  
    if(hFind == INVALID_HANDLE_VALUE)
    {
    return false;
    }
    cstrSubKey=_T("SOFTWARE\\ODBC\\ODBC.INI\\")+cstrSourceName;  


    //先判断要创建的ODBC数据源是否存在
    lReturn=::RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,KEY_READ,&hKey);
    if (lReturn==ERROR_SUCCESS)//打开成功说明已经存在
    {
    //删除它和他的所有子键
    BOOL bRet=RegDelnode(hKey,(LPTSTR)cstrSubKey.GetString());
    if (!bRet)//删除失败
    return FALSE;
    }


    //创建 ODBC数据源在注册表中的子键  
    lReturn=::RegCreateKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)cstrSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dw);  
    if(lReturn != ERROR_SUCCESS)  
    return false; 
    //设置数据源的各项参数  
    CString cstrDbq=cstrSourceDb;  
    CString cstrDriver=sysDir;  
    DWORD dwDriverId=25;  
    CString cstrFil=_T("MS Access") ;  
    CString cstrPWD=cstrPwd;  
    DWORD dwSafeTransactions=0;  
    CString cstrUID=cstrUid;  
    ::RegSetValueEx (hKey,_T("DBQ") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrDbq),cstrDbq .GetLength ()) ;
    ::RegSetValueEx (hKey,_T("Description") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDescription),cstrDescription.GetLength());  
    ::RegSetValueEx (hKey,_T("Driver"),0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrDriver),cstrDriver .GetLength ());  
    ::RegSetValueEx (hKey,_T("DriverId") ,0L,REG_DWORD,(CONST BYTE*)(&dwDriverId),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("FIL") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR) cstrFil),cstrFil .GetLength ()); 
    ::RegSetValueEx (hKey,_T("PWD") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrPWD),cstrPWD.GetLength ()) ; 
    ::RegSetValueEx (hKey,_T("SafeTransactions") ,0L,REG_DWORD,(CONST BYTE*)(&dwSafeTransactions),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("UID") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)cstrUID),cstrUID .GetLength ()); 
    ::RegCloseKey(hKey);  
    //创建 ODBC数据源的 Jet子键  
    cstrSubKey+=_T("\\Engines\\Jet") ;  
    lReturn=::RegCreateKeyEx (HKEY_LOCAL_MACHINE ,(LPCTSTR)cstrSubKey,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dw);  
    if(lReturn != ERROR_SUCCESS)  
    return false;  
    //设置该子键下的各项参数  
    CString strImplict=_T(" ") ;  
    CString strUserCommit=_T("Yes") ;  
    DWORD dwPageTimeout=5;  
    DWORD dwThreads=3;  
    DWORD dwMaxBufferSize=2048;  
    ::RegSetValueEx (hKey,_T("ImplictCommitSync") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)strImplict),strImplict.GetLength ()+1);  
    ::RegSetValueEx (hKey,_T("MaxBufferSize") ,0L,REG_DWORD,(CONST BYTE*)(&dwMaxBufferSize),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("PageTimeout") ,0L,REG_DWORD,(CONST BYTE*)(&dwPageTimeout),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("Threads") ,0L,REG_DWORD,(CONST BYTE*)(&dwThreads),sizeof(dw));  
    ::RegSetValueEx (hKey,_T("UserCommitSync") ,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)strUserCommit),strUserCommit.GetLength ());  
    ::RegCloseKey (hKey);  
    //设置 ODBC数据库引擎名称  
    lReturn=::RegOpenKeyEx (HKEY_LOCAL_MACHINE ,_T("SOFTWARE\\ODBC\\ODBC.INI\\ODBC Data Sources") ,0L,KEY_WRITE,&hKey);  
    if(lReturn !=ERROR_SUCCESS)  
    return false;  
    CString strDbType=_T("Microsoft Access Driver (*.mdb)"); 
    ::RegSetValueEx (hKey,cstrSourceName,0L,REG_SZ,(CONST BYTE*)((LPCTSTR)strDbType),strDbType.GetLength ());  
    return true;  
    }  


    /*
       检测一个系统DSN是否存在
       pszDSN   DSN数据源名称
       pszDBName  数据库文件名(包括完整路径)
       存在返回真,其他返回假
    */
    BOOL IsExistAccessDSN(const char* pszDSN,const char *pszDBName)
    {
    HKEY hkey = {0};


    char szReg[MAXBYTE] = {0};
    strcpy(szReg,"Software\\ODBC\\ODBC.INI\\");
    strcat(szReg, pszDSN);




    //检测系统ODBC数据源是否存在。
    LONG lRet = RegOpenKeyA(HKEY_LOCAL_MACHINE, szReg, &hkey);
    if(lRet == ERROR_SUCCESS)
    {
    DWORD dwcbData=0;
    DWORD dwType=REG_SZ;
    //先查询键值的长度
    lRet= RegQueryValueEx(hkey, (LPTSTR)"DBQ", NULL, &dwType,NULL, &dwcbData);
    if (lRet==ERROR_SUCCESS)
    {
    char *pszData=new char[dwcbData+1];
    RegQueryValueExA(hkey,"DBQ", NULL,&dwType,(LPBYTE)pszData, &dwcbData);
    if(strcmp((const char*)pszData, pszDBName) == 0)
    {       
       delete [] pszData;
    pszData=NULL;
    RegCloseKey(hkey);
    hkey=NULL;
    return TRUE;
    }
    if (pszData)
    {
    delete []pszData;
    pszData=NULL;
    }

    }

    }


    if(hkey != NULL)
    {
    RegCloseKey(hkey);
    hkey=NULL;
    }
    return FALSE;
    }


    /*
      删除一个系统DSN
      pszDSN DSN名称
    note:
      #include <odbcinst.h>
      #pragma comment(lib, "ODBCCP32.lib")
    */


    BOOL DelAccessDSN(const char* pszDSN)
    {
    char szAttr[512]="DSN=";
    strcat(szAttr,pszDSN);
    return SQLConfigDataSource(NULL,ODBC_REMOVE_SYS_DSN,"Microsoft Access Driver (*.mdb)\0",szAttr);
    }




    int _tmain(int argc, _TCHAR* argv[])
    {
    //动态创建SQLite3 DSN数据源
    bool bRet=::SQLConfigDataSource(NULL,ODBC_ADD_SYS_DSN,"SQLite3 ODBC Driver",   
    "DSN=SQLite3 Test\0Database=C:\\Users\\HAO\\Desktop\\test.db\0Description=SQLite3 ODBC数据源\0");
    if (bRet)
    {
    printf("创建SQLite3 Test DSN成功\n");
    }


    bRet=::SQLConfigDataSource(NULL,ODBC_ADD_SYS_DSN,"SQLite3 ODBC Driver",   
    "DSN=Test2\0"   
    "Database=C:\\Users\\HAO\\Desktop\\events2.db\0"   
    "Description=ODBC数据源\0"   
    "DataDirectory=C:\\Users\\HAO\\Desktop\0"   
    "Timeout=1000\0"
    "\0");
    if (bRet)
    {
    printf("创建Test2 DSN成功\n");
    }


    //创建一个Access的DSN数据源
        bRet=CreateAccessDSN("AccessTest","D:\\TestAccess.mdb","这是一个DEMO,创建一个Access的DSN","这是登录ID","这是登录密码");
    //数据源相同时删除以前的然后创建
    bRet=CreateAccessDSN("AccessTest","D:\\TestAccess2.mdb","覆盖掉之前的","UID","PWD");
    bRet=IsExistAccessDSN("AccessTest","D:\\TestAccess2.mdb");
    bRet=DelAccessDSN("AccessTest");
    system("PAUSE");
    return 0;
    }


    展开全文
  • Delphi创建Word2000文档的Demo演示,以Office 2000作为服务器编写,若以 Office XP作为服务器,请在装有 Office XP的...程序中将创建新的Word文档、关闭拼写检查,如果进行拼写检查,将减缓Winword速度、插入数据等操作
  • 以前写过一篇教程,Springboot AOP方式切换多数据源(主从两库类似情况使用最佳): https://blog.csdn.net/qq_35387940/article/details/100122788 网上大多流传的springboot系列的切换多数据源都是以上那种写死...

     以前写过一篇教程,Springboot AOP方式切换多数据源(主从两库类似情况使用最佳):

    https://blog.csdn.net/qq_35387940/article/details/100122788


    网上大多流传的springboot系列的切换多数据源都是以上那种写死在配置文件里的方式,这样如果我需要切换的数据源有10个,那么这种方式会不会显得稍微有点繁琐了。

    现在这篇介绍的流程是,我们把各个数据源的配置信息写在一张数据库表里,从数据库表去加载这些数据源信息,根据我们给每个数据源命名的id去切换数据源,操作对应的数据库。

    OK,接下来我们开始(如果真的想弄懂,最好跟我一步步来)

    首先准备多个数据库,test1 ,test2 ,test3  :

    接下来,我们在test1中,创建表 databasesource ,相关的SQL语句:

    CREATE TABLE `databasesource`  (
      `datasource_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '数据源的id',
      `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '连接信息',
      `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
      `pass_word` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',
      `code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '暂留字段',
      `databasetype` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '数据库类型'
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    

    然后往test1数据库中的表databasesource里填充test2 、test3 这两个数据库的相关配置信息(对应的数据库帐号密码改成自己的),相关的SQL语句:
    ps:这里面的datasource_id的值,是我们后面手动切换数据源的是使用的数据源 id

    INSERT INTO `test1`.`databasesource`(`datasource_id`, `url`, `user_name`, `pass_word`, `code`, `databasetype`) VALUES ('dbtest2', 'jdbc:mysql://localhost:3306/test2?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull', 'root', 'root', NULL, 'mysql');
    INSERT INTO `test1`.`databasesource`(`datasource_id`, `url`, `user_name`, `pass_word`, `code`, `databasetype`) VALUES ('dbtest3', 'jdbc:mysql://localhost:3306/test3?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull', 'root', 'root', NULL, 'mysql');
    

     接下来,我们分别在test2数据库和test3数据库中都创建user表,相关的SQL语句:

    CREATE TABLE `user`  (
      `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `age` int(3) NULL DEFAULT NULL
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

    然后往test2数据库的user表里面填充两条数据用于测试, 相关的SQL语句:

    INSERT INTO `test2`.`user`(`user_name`, `age`) VALUES ('数据库2-小明', 20);
    INSERT INTO `test2`.`user`(`user_name`, `age`) VALUES ('数据库2-小方', 17);
    

    然后往test3数据库的user表里面填充两条数据用于测试, 相关的SQL语句:

    INSERT INTO `test3`.`user`(`user_name`, `age`) VALUES ('数据库3-啊强', 11);
    INSERT INTO `test3`.`user`(`user_name`, `age`) VALUES ('数据库3-啊木', 12);
    

    OK,到这里我们的数据库模拟场景已经准备完毕了,接下来下面就是真正的核心环节了!

    首先是pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.0.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.testDb</groupId>
        <artifactId>dbsource</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>dbsource</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.0.0</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.10</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- druid数据源驱动 1.1.10解决springboot从1.0——2.0版本问题-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    紧接着,application.yml(这里面的数据库配置信息将作为默认数据库):

    spring:
      aop:
        proxy-target-class: true #true为使用CGLIB代理
      datasource:
    
        #nullCatalogMeansCurrent=true&
        url: jdbc:mysql://localhost:3306/test1?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&zeroDateTimeBehavior=convertToNull
        username: root
        password: root
        #新版mysql驱动配置方法
        driverClassName: com.mysql.cj.jdbc.Driver
        ###################以下为druid增加的配置###########################
        type: com.alibaba.druid.pool.DruidDataSource
        # 下面为连接池的补充设置,应用到上面所有数据源中
        # 初始化大小,最小,最大
        initialSize: 5
        minIdle: 5
        maxActive: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        # 打开PSCache,并且指定每个连接上PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        filters: stat,wall,log4j
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
        # 合并多个DruidDataSource的监控数据
        useGlobalDataSourceStat: true
        ###############以上为配置druid添加的配置########################################
    
    mybatis:
      type-aliases-package: com.testdb.dbsource.pojo  #扫描包路径
      configuration:
        map-underscore-to-camel-case: true #打开驼峰命名
      config-location: classpath:mybatis/mybatis-config.xml
    
    server:
      port: 8097
    

     先创建DataSource.java实体类,数据源信息装配的时候用:

    import lombok.Data;
    import lombok.ToString;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/22
     * @Description :
     **/
    @Data
    @ToString
    public class DataSource {
        String datasourceId;
        String url;
        String userName;
        String passWord;
        String code;
        String databasetype;
    }
    

    接下来,创建DruidDBConfig.java:

    这里主要是配置默认的数据源,配置Druid数据库连接池,配置sql工厂加载mybatis的文件,扫描实体类等

    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    import javax.sql.DataSource;
    import java.sql.SQLException;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/22
     * @Description :
     **/
    @Configuration
    @EnableTransactionManagement
    public class DruidDBConfig {
        private final Logger log = LoggerFactory.getLogger(getClass());
    
        // adi数据库连接信息
        @Value("${spring.datasource.url}")
        private String dbUrl;
        @Value("${spring.datasource.username}")
        private String username;
        @Value("${spring.datasource.password}")
        private String password;
        @Value("${spring.datasource.driverClassName}")
        private String driverClassName;
        // 连接池连接信息
        @Value("${spring.datasource.initialSize}")
        private int initialSize;
        @Value("${spring.datasource.minIdle}")
        private int minIdle;
        @Value("${spring.datasource.maxActive}")
        private int maxActive;
        @Value("${spring.datasource.maxWait}")
        private int maxWait;
    
        @Bean // 声明其为Bean实例
        @Primary // 在同样的DataSource中,首先使用被标注的DataSource
        @Qualifier("mainDataSource")
        public DataSource dataSource() throws SQLException {
            DruidDataSource datasource = new DruidDataSource();
            // 基础连接信息
            datasource.setUrl(this.dbUrl);
            datasource.setUsername(username);
            datasource.setPassword(password);
            datasource.setDriverClassName(driverClassName);
            // 连接池连接信息
            datasource.setInitialSize(initialSize);
            datasource.setMinIdle(minIdle);
            datasource.setMaxActive(maxActive);
            datasource.setMaxWait(maxWait);
            datasource.setPoolPreparedStatements(true); //是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
            datasource.setMaxPoolPreparedStatementPerConnectionSize(20);
            //  datasource.setConnectionProperties("oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout=60000");//对于耗时长的查询sql,会受限于ReadTimeout的控制,单位毫秒
            datasource.setConnectionProperties("druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000");//对于耗时长的查询sql,会受限于ReadTimeout的控制,单位毫秒
            datasource.setTestOnBorrow(true); //申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用
            datasource.setTestWhileIdle(true);//建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
            String validationQuery = "select 1 from dual";
            datasource.setValidationQuery(validationQuery); //用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
            datasource.setFilters("stat,wall");//属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
            datasource.setTimeBetweenEvictionRunsMillis(60000); //配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
            datasource.setMinEvictableIdleTimeMillis(180000); //配置一个连接在池中最小生存的时间,单位是毫秒,这里配置为3分钟180000
            datasource.setKeepAlive(true); //打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
            datasource.setRemoveAbandoned(true); //是否移除泄露的连接/超过时间限制是否回收。
            datasource.setRemoveAbandonedTimeout(3600); //泄露连接的定义时间(要超过最大事务的处理时间);单位为秒。这里配置为1小时
            datasource.setLogAbandoned(true); 移除泄露连接发生是是否记录日志
            return datasource;
    
    
        }
    
    
        /**
         * 注册一个StatViewServlet    druid监控页面配置1-帐号密码配置
         *
         * @return servlet registration bean
         */
        @Bean
        public ServletRegistrationBean druidStatViewServlet() {
            ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
                    new StatViewServlet(), "/druid/*");
            servletRegistrationBean.addInitParameter("loginUsername", "admin");
            servletRegistrationBean.addInitParameter("loginPassword", "123456");
            servletRegistrationBean.addInitParameter("resetEnable", "false");
            return servletRegistrationBean;
        }
    
        /**
         * 注册一个:filterRegistrationBean   druid监控页面配置2-允许页面正常浏览
         *
         * @return filter registration bean
         */
        @Bean
        public FilterRegistrationBean druidStatFilter() {
            FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(
                    new WebStatFilter());
            // 添加过滤规则.
            filterRegistrationBean.addUrlPatterns("/*");
            // 添加不需要忽略的格式信息.
            filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
            return filterRegistrationBean;
        }
    
        @Bean(name = "dynamicDataSource")
        @Qualifier("dynamicDataSource")
        public DynamicDataSource dynamicDataSource() throws SQLException {
            DynamicDataSource dynamicDataSource = new DynamicDataSource();
            dynamicDataSource.setDebug(false);
            //配置缺省的数据源
            // 默认数据源配置 DefaultTargetDataSource
            dynamicDataSource.setDefaultTargetDataSource(dataSource());
            Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
            //额外数据源配置 TargetDataSources
            targetDataSources.put("mainDataSource", dataSource());
            dynamicDataSource.setTargetDataSources(targetDataSources);
            return dynamicDataSource;
        }
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dynamicDataSource());
            //解决手动创建数据源后字段到bean属性名驼峰命名转换失效的问题
            sqlSessionFactoryBean.setConfiguration(configuration());
    
            // 设置mybatis的主配置文件
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            // Resource mybatisConfigXml = resolver.getResource("classpath:mybatis/mybatis-config.xml");
            //  sqlSessionFactoryBean.setConfigLocation(mybatisConfigXml);
            // 设置别名包
            //  sqlSessionFactoryBean.setTypeAliasesPackage("com.testdb.dbsource.pojo");
    
            //手动配置mybatis的mapper.xml资源路径,如果单纯使用注解方式,不需要配置该行
            // sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mybatis/mapper/*.xml"));
            return sqlSessionFactoryBean.getObject();
        }
    
    
        /**
         * 读取驼峰命名设置
         *
         * @return
         */
        @Bean
        @ConfigurationProperties(prefix = "mybatis.configuration")
        public org.apache.ibatis.session.Configuration configuration() {
            return new org.apache.ibatis.session.Configuration();
        }
    
    
    }
    

     然后是用于手动切换数据源的 DBContextHolder.java:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/22
     * @Description :
     **/
    public class DBContextHolder {
        private final static Logger log = LoggerFactory.getLogger(DBContextHolder.class);
        // 对当前线程的操作-线程安全的
        private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
    
        // 调用此方法,切换数据源
        public static void setDataSource(String dataSource) {
            contextHolder.set(dataSource);
            log.info("已切换到数据源:{}",dataSource);
        }
    
        // 获取数据源
        public static String getDataSource() {
            return contextHolder.get();
        }
    
        // 删除数据源
        public static void clearDataSource() {
            contextHolder.remove();
            log.info("已切换到主数据源");
        }
    
    }
    

    然后是核心,手动加载默认数据源、创建数据源连接、检查数据源连接、删除数据源连接等 ,DynamicDataSource.java:

    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.stat.DruidDataSourceStatManager;
    import com.testdb.dbsource.pojo.DataSource;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    import org.springframework.util.StringUtils;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.util.Map;
    import java.util.Set;
    
    
    public class DynamicDataSource extends AbstractRoutingDataSource {
        private boolean debug = true;
        private final Logger log = LoggerFactory.getLogger(getClass());
        private Map<Object, Object> dynamicTargetDataSources;
        private Object dynamicDefaultTargetDataSource;
    
    
        @Override
        protected Object determineCurrentLookupKey() {
            String datasource = DBContextHolder.getDataSource();
            if (!StringUtils.isEmpty(datasource)) {
                Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
                if (dynamicTargetDataSources2.containsKey(datasource)) {
                    log.info("---当前数据源:" + datasource + "---");
                } else {
                    log.info("不存在的数据源:");
                    return null;
    //                    throw new ADIException("不存在的数据源:"+datasource,500);
                }
            } else {
                log.info("---当前数据源:默认数据源---");
            }
    
            return datasource;
        }
    
        @Override
        public void setTargetDataSources(Map<Object, Object> targetDataSources) {
    
            super.setTargetDataSources(targetDataSources);
    
            this.dynamicTargetDataSources = targetDataSources;
    
        }
    
    
        // 创建数据源
        public boolean createDataSource(String key, String driveClass, String url, String username, String password, String databasetype) {
            try {
                try { // 排除连接不上的错误
                    Class.forName(driveClass);
                    DriverManager.getConnection(url, username, password);// 相当于连接数据库
    
                } catch (Exception e) {
    
                    return false;
                }
                @SuppressWarnings("resource")
    //            HikariDataSource druidDataSource = new HikariDataSource();
                        DruidDataSource druidDataSource = new DruidDataSource();
                druidDataSource.setName(key);
                druidDataSource.setDriverClassName(driveClass);
                druidDataSource.setUrl(url);
                druidDataSource.setUsername(username);
                druidDataSource.setPassword(password);
                druidDataSource.setInitialSize(1); //初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
                druidDataSource.setMaxActive(20); //最大连接池数量
                druidDataSource.setMaxWait(60000); //获取连接时最大等待时间,单位毫秒。当链接数已经达到了最大链接数的时候,应用如果还要获取链接就会出现等待的现象,等待链接释放并回到链接池,如果等待的时间过长就应该踢掉这个等待,不然应用很可能出现雪崩现象
                druidDataSource.setMinIdle(5); //最小连接池数量
                String validationQuery = "select 1 from dual";
    //            if("mysql".equalsIgnoreCase(databasetype)) {
    //                driveClass = DBUtil.mysqldriver;
    //                validationQuery = "select 1";
    //            } else if("oracle".equalsIgnoreCase(databasetype)){
    //                driveClass = DBUtil.oracledriver;
    //                druidDataSource.setPoolPreparedStatements(true); //是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
    //                druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(50);
    //                int sqlQueryTimeout = ADIPropUtil.sqlQueryTimeOut();
    //                druidDataSource.setConnectionProperties("oracle.net.CONNECT_TIMEOUT=6000;oracle.jdbc.ReadTimeout="+sqlQueryTimeout);//对于耗时长的查询sql,会受限于ReadTimeout的控制,单位毫秒
    //            } else if("sqlserver2000".equalsIgnoreCase(databasetype)){
    //                driveClass = DBUtil.sql2000driver;
    //                validationQuery = "select 1";
    //            } else if("sqlserver".equalsIgnoreCase(databasetype)){
    //                driveClass = DBUtil.sql2005driver;
    //                validationQuery = "select 1";
    //            }
                druidDataSource.setTestOnBorrow(true); //申请连接时执行validationQuery检测连接是否有效,这里建议配置为TRUE,防止取到的连接不可用
                druidDataSource.setTestWhileIdle(true);//建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
                druidDataSource.setValidationQuery(validationQuery); //用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
                druidDataSource.setFilters("stat");//属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
                druidDataSource.setTimeBetweenEvictionRunsMillis(60000); //配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
                druidDataSource.setMinEvictableIdleTimeMillis(180000); //配置一个连接在池中最小生存的时间,单位是毫秒,这里配置为3分钟180000
                druidDataSource.setKeepAlive(true); //打开druid.keepAlive之后,当连接池空闲时,池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作,即执行druid.validationQuery指定的查询SQL,一般为select * from dual,只要minEvictableIdleTimeMillis设置的小于防火墙切断连接时间,就可以保证当连接空闲时自动做保活检测,不会被防火墙切断
                druidDataSource.setRemoveAbandoned(true); //是否移除泄露的连接/超过时间限制是否回收。
                druidDataSource.setRemoveAbandonedTimeout(3600); //泄露连接的定义时间(要超过最大事务的处理时间);单位为秒。这里配置为1小时
                druidDataSource.setLogAbandoned(true); 移除泄露连接发生是是否记录日志
                druidDataSource.init();
                this.dynamicTargetDataSources.put(key, druidDataSource);
                setTargetDataSources(this.dynamicTargetDataSources);// 将map赋值给父类的TargetDataSources
                super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
                log.info(key+"数据源初始化成功");
                //log.info(key+"数据源的概况:"+druidDataSource.dump());
                return true;
            } catch (Exception e) {
                log.error(e + "");
                return false;
            }
        }
        // 删除数据源
        public boolean delDatasources(String datasourceid) {
            Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
            if (dynamicTargetDataSources2.containsKey(datasourceid)) {
                Set<DruidDataSource> druidDataSourceInstances = DruidDataSourceStatManager.getDruidDataSourceInstances();
                for (DruidDataSource l : druidDataSourceInstances) {
                    if (datasourceid.equals(l.getName())) {
                        dynamicTargetDataSources2.remove(datasourceid);
                        DruidDataSourceStatManager.removeDataSource(l);
                        setTargetDataSources(dynamicTargetDataSources2);// 将map赋值给父类的TargetDataSources
                        super.afterPropertiesSet();// 将TargetDataSources中的连接信息放入resolvedDataSources管理
                        return true;
                    }
                }
                return false;
            } else {
                return false;
            }
        }
    
        // 测试数据源连接是否有效
        public boolean testDatasource(String key, String driveClass, String url, String username, String password) {
            try {
                Class.forName(driveClass);
                DriverManager.getConnection(url, username, password);
                return true;
            } catch (Exception e) {
                return false;
            }
        }
        
        @Override
        public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
            super.setDefaultTargetDataSource(defaultTargetDataSource);
            this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
        }
    
        /**
         * @param debug
         *            the debug to set
         */
        public void setDebug(boolean debug) {
            this.debug = debug;
        }
    
        /**
         * @return the debug
         */
        public boolean isDebug() {
            return debug;
        }
    
        /**
         * @return the dynamicTargetDataSources
         */
        public Map<Object, Object> getDynamicTargetDataSources() {
            return dynamicTargetDataSources;
        }
    
        /**
         * @param dynamicTargetDataSources
         *            the dynamicTargetDataSources to set
         */
        public void setDynamicTargetDataSources(Map<Object, Object> dynamicTargetDataSources) {
            this.dynamicTargetDataSources = dynamicTargetDataSources;
        }
    
        /**
         * @return the dynamicDefaultTargetDataSource
         */
        public Object getDynamicDefaultTargetDataSource() {
            return dynamicDefaultTargetDataSource;
        }
    
        /**
         * @param dynamicDefaultTargetDataSource
         *            the dynamicDefaultTargetDataSource to set
         */
        public void setDynamicDefaultTargetDataSource(Object dynamicDefaultTargetDataSource) {
            this.dynamicDefaultTargetDataSource = dynamicDefaultTargetDataSource;
        }
    
        public void createDataSourceWithCheck(DataSource dataSource) throws Exception {
            String datasourceId = dataSource.getDatasourceId();
            log.info("正在检查数据源:"+datasourceId);
            Map<Object, Object> dynamicTargetDataSources2 = this.dynamicTargetDataSources;
            if (dynamicTargetDataSources2.containsKey(datasourceId)) {
                log.info("数据源"+datasourceId+"之前已经创建,准备测试数据源是否正常...");
                //DataSource druidDataSource = (DataSource) dynamicTargetDataSources2.get(datasourceId);
                DruidDataSource druidDataSource = (DruidDataSource) dynamicTargetDataSources2.get(datasourceId);
                boolean rightFlag = true;
                Connection connection = null;
                try {
                    log.info(datasourceId+"数据源的概况->当前闲置连接数:"+druidDataSource.getPoolingCount());
                    long activeCount = druidDataSource.getActiveCount();
                    log.info(datasourceId+"数据源的概况->当前活动连接数:"+activeCount);
                    if(activeCount > 0) {
                        log.info(datasourceId+"数据源的概况->活跃连接堆栈信息:"+druidDataSource.getActiveConnectionStackTrace());
                    }
                    log.info("准备获取数据库连接...");
                    connection = druidDataSource.getConnection();
                    log.info("数据源"+datasourceId+"正常");
                } catch (Exception e) {
                    log.error(e.getMessage(),e); //把异常信息打印到日志文件
                    rightFlag = false;
                    log.info("缓存数据源"+datasourceId+"已失效,准备删除...");
                    if(delDatasources(datasourceId)) {
                        log.info("缓存数据源删除成功");
                    } else {
                        log.info("缓存数据源删除失败");
                    }
                } finally {
                    if(null != connection) {
                        connection.close();
                    }
                }
                if(rightFlag) {
                    log.info("不需要重新创建数据源");
                    return;
                } else {
                    log.info("准备重新创建数据源...");
                    createDataSource(dataSource);
                    log.info("重新创建数据源完成");
                }
            } else {
                createDataSource(dataSource);
            }
    
        }
    
        private  void createDataSource(DataSource dataSource) throws Exception {
            String datasourceId = dataSource.getDatasourceId();
            log.info("准备创建数据源"+datasourceId);
            String databasetype = dataSource.getDatabasetype();
            String username = dataSource.getUserName();
            String password = dataSource.getPassWord();
            String url = dataSource.getUrl();
            String driveClass = "com.mysql.cj.jdbc.Driver";
    //        if("mysql".equalsIgnoreCase(databasetype)) {
    //            driveClass = DBUtil.mysqldriver;
    //        } else if("oracle".equalsIgnoreCase(databasetype)){
    //            driveClass = DBUtil.oracledriver;
    //        }  else if("sqlserver2000".equalsIgnoreCase(databasetype)){
    //            driveClass = DBUtil.sql2000driver;
    //        } else if("sqlserver".equalsIgnoreCase(databasetype)){
    //            driveClass = DBUtil.sql2005driver;
    //        }
            if(testDatasource(datasourceId,driveClass,url,username,password)) {
                boolean result = this.createDataSource(datasourceId, driveClass, url, username, password, databasetype);
                if(!result) {
                    log.error("数据源"+datasourceId+"配置正确,但是创建失败");
    //                throw new ADIException("数据源"+datasourceId+"配置正确,但是创建失败",500);
                }
            } else {
                log.error("数据源配置有错误");
    //            throw new ADIException("数据源配置有错误",500);
            }
        }
    
    }
    
    

    ok,然后是我们切换数据源使用的方法, 我们这里采用mybatis注解的方式获取test1数据库里的databasesource 表信息,然后根据我们传入对应的数据源id进行数据源切换:

    DataSourceMapper.java :

    import com.testdb.dbsource.pojo.DataSource;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Select;
    import java.util.List;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/23
     * @Description :
     **/
    @Mapper
    public interface DataSourceMapper {
    
        @Select("SELECT * FROM databasesource")
        List<DataSource> get();
    
    
    }

    DBChangeService.java:

    import com.testdb.dbsource.pojo.DataSource;
    import java.util.List;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/22
     * @Description :
     **/
    
    public interface DBChangeService {
    
        List<DataSource> get();
    
        boolean changeDb(String datasourceId) throws Exception;
    
    }
    

    DBChangeServiceImpl.java:

    import com.testdb.dbsource.dbconfig.DBContextHolder;
    import com.testdb.dbsource.dbconfig.DynamicDataSource;
    import com.testdb.dbsource.mapper.DataSourceMapper;
    import com.testdb.dbsource.pojo.DataSource;
    import com.testdb.dbsource.service.DBChangeService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import java.util.List;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/22
     * @Description :
     **/
    @Service
    public class DBChangeServiceImpl implements DBChangeService {
    
       @Autowired
       DataSourceMapper dataSourceMapper;
        @Autowired
        private DynamicDataSource dynamicDataSource;
        @Override
        public List<DataSource> get() {
            return dataSourceMapper.get();
        }
    
        @Override
        public boolean changeDb(String datasourceId) throws Exception {
    
            //默认切换到主数据源,进行整体资源的查找
            DBContextHolder.clearDataSource();
    
            List<DataSource> dataSourcesList = dataSourceMapper.get();
    
            for (DataSource dataSource : dataSourcesList) {
                if (dataSource.getDatasourceId().equals(datasourceId)) {
                    System.out.println("需要使用的的数据源已经找到,datasourceId是:" + dataSource.getDatasourceId());
                    //创建数据源连接&检查 若存在则不需重新创建
                    dynamicDataSource.createDataSourceWithCheck(dataSource);
                    //切换到该数据源
                    DBContextHolder.setDataSource(dataSource.getDatasourceId());
                    return true;
                }
            }
            return false;
    
        }
    
    }

    注意认真看看上面的changeDb这个方法里面的代码,这就是后续手动切换调用的方法。

     

    接下来,写相关操作user表的代码,因为user表分别在test2、test3数据库里,这样用于我们切换到test2或者test3数据库操作这些数据。

    User.java:

    import lombok.Data;
    import lombok.ToString;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/22
     * @Description :
     **/
    
    @Data
    @ToString
    public class User {
    
        String userName;
        String age;
    }
    

    UserMappper.java  (上面简单介绍了下使用注解的方式获取表数据,可能有些人不习惯,那么这里也使用传统的mapper.xml方式编写mysql语句):

    import com.testdb.dbsource.pojo.User;
    import org.apache.ibatis.annotations.Mapper;
    import java.util.List;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/23
     * @Description :
     **/
    @Mapper
    public interface UserMapper {
    
        List<User> queryUserInfo();
    
    
        
    }

    userMapper.xml(注意namespace命名空间对应的路径以及user实体类对应的路径):

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.testdb.dbsource.mapper.UserMapper">
        
        <!--查询所有用户信息-->
        <select id="queryUserInfo" resultType="com.testdb.dbsource.pojo.User">
           select *
           from user
        </select>
    
        
    </mapper>
    

    顺便一提,我的mapper.xml放在了下面的这个目录结果里,这里的目录结构路径非常关键,因为我们是手动切换数据源,采取了手动配置SqlSessionFactory,需要我们自己去配置mapper.xml路径的,一开始的DruidDBConfig里面有相关的代码,可以去回顾下:

    ps:
    DruidDBConfig.java

     

    顺带,mybatis-config.xml里面,我做了简单的配置:

    其实关于驼峰命名方式开启,我们在手动配置的时候也特意做了配置代码的。

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE configuration
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration>
    
        <settings>
            <setting name="useGeneratedKeys" value="true"/>
            <setting name="useColumnLabel" value="true"/>
            <setting name="mapUnderscoreToCamelCase" value="true"/>
        </settings>
    </configuration>
    
    
    

    到这里,我们已经可以开始测试,

    创建UserController.java,写个简单的测试接口:

    import com.testdb.dbsource.dbconfig.DBContextHolder;
    import com.testdb.dbsource.pojo.User;
    import com.testdb.dbsource.service.DBChangeService;
    import com.testdb.dbsource.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import java.util.List;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2019/10/23
     * @Description :
     **/
    
    @RestController
    public class UserController {
    
    
        @Autowired
        private DBChangeService dbChangeServiceImpl;
        @Autowired
        UserService userService;
    
    
    
    
        /**
         * 查询所有
         * @return
         */
        @GetMapping("/test")
        public  String test() throws Exception {
    
            //切换到数据库dbtest2
            String datasourceId="dbtest2";
            dbChangeServiceImpl.changeDb(datasourceId);
            List<User> userList= userService.queryUserInfo();
            System.out.println(userList.toString());
    
            //再切换到数据库dbtest3
            dbChangeServiceImpl.changeDb("dbtest3");
            List<User> userList3= userService.queryUserInfo();
            System.out.println(userList3.toString());
    
    
            //切回主数据源
            DBContextHolder.clearDataSource();
            return "ok";
        }
    
    }
    

     主要看代码注释,调用整合出来的changeDb方法,通过传送数据源id (dbtest2)

    切换到对应的数据库,然后操作对应数据库。

     

    项目运行起来,调用接口 /test :

     

    然后我们来看看控制台输出内容:

    OK,非常简单顺利,教程就到此。 

     

    补充该实战教学的事务相关介绍配置和介绍:

    实现动态数据源事务,找到该篇项目实例中的DruidDBConfig.java ,

    将 我们实现的动态数据源加载类DynamicDataSource 添加到数据事务管理器里:

        @Bean
        public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
            return new DataSourceTransactionManager(dynamicDataSource);
        }

     

    事务测试  (单个数据源的事务)

     

    接着,给我们的新增User方法里面,故意搞个异常出来,测试下回滚是否生效:

    在不指定任何异常时,使用注解事务,默认只对RuntimeException异常生效,所以咱们简单点,在啥时候想回滚,咱们就手动丢个RuntimeException异常出来。

    写个测试接口,给db2 插入一条user信息,看看事务回滚情况:
     

        /**
         * 添加
         * @return
         */
        @GetMapping("/addTest")
        public  String addTest() throws Exception {
    
            //切换到数据库dbtest2
            String datasourceId="dbtest2";
            dbChangeServiceImpl.changeDb(datasourceId);
            User user2=new User();
            user2.setUserName("db2用户");
            user2.setAge("11");
            userService.insertUser(user2);
            //切回主数据源
            DBContextHolder.clearDataSource();
            return "ok";
        }
    

    调用该接口,可以看到在未抛出异常时,可以看到插入根据影响行数,提示是成功了,但后面咱们抛异常触发了事务回滚:

    看下数据库没出现新增的数据,事务回滚成功:

     

     

    事务测试  (多个数据源的事务)

     

    可以上图这个场景,就是测试切换不同数据源的时候,目前这两个数据源的插入方法都开启了事务,我们把test2的插入User方法加了故意抛出异常触发事务的代码,看看对于不同数据源事务触发情况: 

    看看调用该接口,控制台的输出:

    再看看数据库情况,

    test2数据库,

     

    test3数据库,

     

    可以看到在多个数据源的时候,还是只有单独的数据自己的事务起作用了(怎么解决这种情况? 文章末尾有介绍)。

     

    如果只是用于主从数据库两个数据源的业务场景,那么该篇非常适合使用,因为只需保证主的事务,从库会同步数据。
    而且如果仅仅是为了满足主从/读写的场景,大可不必从数据库读取数据源,使用AOP方式读取配置文件的数据源即可满足业务场景,也就是参考我这篇:https://blog.csdn.net/qq_35387940/article/details/100122788 

     

     

    那该篇的动态数据源实战适合哪些场景呢?


    一.业务场景需要 很多个不同数据源,进行数据获取,不仅仅是两个,这样一来在配置文件配置是基本不可取的。

    二.进行多从数据源数据获取,加上逻辑分析筛选后, 插入或更新 某个数据库,只需为该数据库事务进行负责
     

    例如我的使用场景:

    公司各个小项目很多a,b,c,d,e,都用着自己项目的数据库。

    而新项目接口需求,收到第三方的调用后,需要到a,b,c,d,e系统的数据库里读取出不同的核心数据,然后进行业务逻辑分析,然后生成新的订单,插入到新项目的主数据库里,并返回结果给第三方,我只需要对一个数据进行事务管理。

     

    那如果就是想多个数据源事务可以一块回滚呢? 难道就没解决方案了吗?

    很可以,就是需要有这种精神,不管你目前的业务场景有没有触及。值得敬佩的你,请看:

    并不然,使用JTA分布式事务即可解决。
    请看我这篇:

    Springboot 整合druid+mybatis+jta分布式事务+多数据源aop注解动态切换 (一篇到位)

    https://blog.csdn.net/qq_35387940/article/details/103474353

     

     

     

    PS: 这篇文章我很久前写的,日常搬砖也比较忙,但是从评论里包括我其他的动态切换数据源文章里面得知,大家使用mybatis-plus来开发还是比较多的。

    不了解mybatis-plus的可以看看我这篇简单的入门使用(https://blog.csdn.net/qq_35387940/article/details/103381713)。

    回归这个问题,那么在该篇文章里,如果使用 mybatis-plus 的话,为了保证使用,需要做什么调整呢?

     

    1. 找到配置文件 

    2.把本文原先的 SqlSessionFactoryBean 调整为使用  MybatisSqlSessionFactoryBean ,其实也就是说换一下sqlSessionFactory()这个方法的代码即可:

    代码:
     

        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
    
            MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dynamicDataSource());
            sqlSessionFactoryBean.setMapperLocations(
                    new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
            // 设置mybatis的主配置文件
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource mybatisConfigXml = resolver.getResource("classpath:mybatis/mybatis-config.xml");
            sqlSessionFactoryBean.setConfigLocation(mybatisConfigXml);
    
            return sqlSessionFactoryBean.getObject();
        }
    

     

    ok,我们来简单写个接口测试一下,

    这里新建一个Mapper继承mybatis-plus里面的BaseMapper,service层我就省略了。

    然后写个测试接口,从主数据源分别切换到db2和db3 ,分别插入一下数据:

    import com.testdb.dbsource.dbconfig.DBContextHolder;
    import com.testdb.dbsource.pojo.User;
    import com.testdb.dbsource.service.DBChangeService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * @Author : JCccc
     * @CreateTime : 2020/10/10
     * @Description :
     **/
    
    @RestController
    public class UserPlusController {
        @Autowired
        private DBChangeService dbChangeServiceImpl;
        @Autowired
        UserPlusMapper userMapper;
        @GetMapping("/testPlus")
        public void testPlus() throws Exception {
            User User2 = new User();
            User2.setUserName("TEST insert db 2");
            User2.setAge("2");
            //切换到数据库dbtest2
            String datasourceId2="dbtest2";
            dbChangeServiceImpl.changeDb(datasourceId2);
            int effectNum2 = userMapper.insert(User2);
            System.out.println("db2添加后的影响行数:"+effectNum2);
    
    
            //切换到数据库dbtest3
            String datasourceId3="dbtest3";
            dbChangeServiceImpl.changeDb(datasourceId3);
            User User3 = new User();
            User3.setUserName("TEST insert db 3");
            User3.setAge("3");
            int effectNum3 = userMapper.insert(User3);
            System.out.println("db3添加后的影响行数:"+effectNum3);
    
    
    
            //切回主数据源
            DBContextHolder.clearDataSource();
        }
    
    }
    

    调用接口,控制台打印:

    然后是数据库分别都有了数据:

     

    ok,那这个对于mybatis-plus的使用补充就到这。 

    展开全文
  • 手动配置和自动配置ODBC数据源(C++)

    千次阅读 2019-07-14 12:10:10
    一般来说,我们在使用ODBC时,都是手动配置数据源,也就是使用电脑自带的ODBC数据源窗口进行手动配置。当我们在进行项目开发的时候,编好的代码给用户时,用户需要重新配置数据源,十分麻烦,为了方便,我们可以通过...

    一般来说,我们在使用ODBC时,都是手动配置数据源,也就是使用电脑自带的ODBC数据源窗口进行手动配置。当我们在进行项目开发的时候,编好的代码给用户时,用户需要重新配置数据源,十分麻烦,为了方便,我们可以通过代码来解决这一问题,直接通过编程自动配置。

    一、手动配置数据源

    打开odbc数据源,注意32位和64位,根据自己的项目而定,我选择的是64位。
    在这里插入图片描述
    点击添加,根据自己的数据格式选择对应的驱动程序,我这里选择Microsoft Access Driver (*.mdb, *.accdb);点击完成,弹出对话框,输入源文件名(自己起一个名字,程序就是通过这个名字连接数据库,我这里使用MyAccess),选择数据库(选择数据的路径,我这里选择D:\02.mdb),点击高级设置用户名、密码等,我这里不设置了。在这里插入图片描述
    数据源添加完成,我们在用户数据源中能看到自己添加的数据源了。在这里插入图片描述

    二、自动配置数据源–SQLConfigDataSource()函数

    自动配置数据源也比较简单,使用SQLConfigDataSource()函数就可以搞定,配置成功的关键在于这个函数的参数一定要写对,不然怎么配置都无法成功。

    1. SQLConfigDataSource 函数说明
    BOOL SQLConfigDataSource ( HWND hwndParent,WORD fRequest,
    LPCSTR lpszDriver,LPCSTR lpszAttributes );
    参数1:父窗口句柄,一般不需要创建数据源对话框,可设为NULL。
    参数2:指定操作类型。增加、删除、修改数据源等(分用户数据源和系统数据源)。
    参数3:指定ODBC数据源的驱动。
    参数4:指定ODBC数据源的属性,数据源名、路径、数据类型、数据描述等。

    配置Access数据源的代码参数如下:
    BOOL isSucess = SQLConfigDataSource(NULL, ODBC_ADD_DSN, “Microsoft Access Driver (*.mdb, *.accdb)\0”, “DSN=MyAccess\0 DBQ=d:\02.mdb\0”);
    返回1表示成功,返回0表示失败。

    特别注意:
    1.使用此函数需要包含 #include “odbcinst.h” 头文件
    2.第三个参数为ODBC驱动程序,这个字符串一定不能有任何错误,包括空格都不能缺少,它一定要和手动配置时候出现的驱动程序一样,不然就连接不上。
    在这里插入图片描述

    展开全文
  • 数据导出到Excel(或Word代码大全 标签: exceldatasetnulloffice数据库generation 2008-11-16 15:24 13988人阅读 评论(15) 收藏 举报  分类:   数据库应用开发(24) Office(9) ASP ...
  • springboot 链接多数据库,动态获取数据源信息

    千次阅读 热门讨论 2019-04-09 18:12:32
    springboot 链接多数据库,动态获取数据源信息 前言 最近公司有个需求需要后端访问多个数据库,在网上查了半天资料,结果发现大部分都是配置的主从库这种数据源固定的情况,不符合我的需求,假设我们有这样一种...
  • word2vec 代码 完整注释

    千次阅读 2018-09-28 16:47:17
    前几天看了几篇kdd的文章,尼玛,基本全部设计都...好吧,那还得好好看看语言方面的embedding,最著名的,当然就是大名鼎鼎的word2vector了,于是,花了两三天的时间,看了一些数学原理和代码,其实网络上的...
  • ES2010引用MYSQL外部数据源教程 整理:安哥 qq:29154754 zxa2000@163.com 20130827 一安装mysql的ODBC驱动程序mysql官网有下载 二mysqlODBC数据源配置 1mysql驱动程序安装好后点开始/管理工具/数据源添加 点完成 ...
  • Spark之SparkStreaming数据源

    千次阅读 2019-06-05 09:26:20
    SparkStreaming的数据源 文件 Flume Kafka: DStreams输入Spark Streaming原生支持一些不同的数据源。一些“核心”数据源已经被打包到Spark Streaming 的 Maven 工件中,而其他的一些则可以通过 spark-streaming-...
  • 读取Excel 数据并写入到Word示例

    千次阅读 2019-10-14 19:44:05
    一个读取Excel 数据并写入到Word 的项目
  • Delphi向Word导出数据

    千次阅读 2008-03-28 18:00:00
    最近客户要求将数据导出到word,查找了一些代码后做出来了.先将方法共享出来.希望对大家有用. procedure TFrmWeekAnalysisQry.BtnExportToExcelClick(Sender: TObject);var wordApp,WordDoc,WrdSelection:variant;...
  • public string CreateWordFile(string CheckedInfo)  {  string message = "";  try  {  Object Nothing = System.Reflection.Missing.Value;  
  • JAVA局域网监听软件的设计与开发(代码+WORD论文) 摘 要 网络监听软件是提供给网络安全管理人员进行安全管理的工具,可以用来监视网络的状态、数据流动情况以及网络上传输的信息,以获取有用信息。作为黑客来说,...
  • pd导出word表跟一般软件的导出...需要先通过powerdesigner的反向工程,根据数据库生成physical model,在反向工程之前先建好数据源 1.1、创建数据源 1、database》configureConnections 2、选择,connection ...
  • 如何创建ODBC数据源

    千次阅读 2010-05-13 13:52:00
    在Windows的系统目录中有ODBC数据库引擎Odbcjt32.dll,支持...如果在应用程序中需要使用ODBC来存取数据库,那么在安装过程中就只要创建数据库的数据源。 创建ODBC数据源可以调用Windows系统目录下的动态链接库Odbc
  • 我目前的的需求就是向一个word文档中插入一个饼图,该使用哪个包来实现? 个人目前是使用python-docx这个包来生成了word文档,但是这个包貌似没有生成图表的方法,求指点 备注:这里插入的饼图需要是一个图表chart...
  • 1、多数据简介 实际的项目中,经常会用到不同的数据库以满足项目的实际需求。随着业务的并发量的不断增加,一个项目使用多个数据库:主从复制、读写分离、分布式数据库等方式,越来越常见。 2、MybatisPlus简介 ...
  • 方法一:使用Office Word和Excel打印成绩通知单和成绩条(2008-11-08 15:49:52)标签:电脑 成绩通知单 this if word excel 邮件合并 分数条 教育 分类:他山之石 每到期末,班主任或者辅导员就要开始收集...
  • 添加DSN 时,如果只有SQL Server选项(除非你使用的仅仅是这个数据库),比如博主需要使用.mdb 数据文件。需要安装64位的数据库连接引擎。 提出SPSS 位数的原因如下: 在ODBC(32位)下设置了DSN,在64位的SPSS ...
  • Word给自动生成的目录页码添加括号

    千次阅读 2020-02-18 22:09:07
    Word给自动生成的目录页码添加括号 一些学校的毕业论文格式要求中,要求目录的页码需要加括号如下: [外链图片转存失败,站可能有防盗链机制,建议将图片保存下来直接上传(img-kW5SI4pw-1582034649079)(C:\Users\...
  • Spark Streaming用于流式数据的处理,SparkStreaming支持的数据源很多,例如Kafka、Flume、Twitter、ZeroMQ和简单的TCP套接字等等,数据输入后可以用Spark的高度抽象原语如:map、reduce、join、window等进行运算。...
  • VC动态建立ODBC数据源

    千次阅读 2011-07-12 17:25:38
    动态创建数据源需要用到的API:SQLConfigDataSource,需要包含的头文件为:#include ,需要包含静态库:#pragma comment(lib, "ODBCCP32.lib")。 SQLConfigDataSource 函数说明 ODBC API提
  • 1、往xml文件中添加数据占位时,不细心导致文件中出现多余的{、}、;、#等字符,导致xml校验错误,会导致生成的word打不开。 2、word中有超链接,链接中包含多个参数时,会用&进行连接,然而在xml中&属于...
  •  anydac 未发现数据源名称如何处理 2.原因分析  操作系统的问题,我的是64位的系统,plsql支持32位的odbc驱动! 3.解决方案  第一步:运行C:\Windows\SysWOW64\odbcad32.exe  出现这个界面  第二步...
  • WORD调用EXCEL数据

    千次阅读 2012-05-11 18:12:10
    当然,我们不否认很多专业化的考证制作工具已经完全把这项任务承担了,但是作为Word中实用又很重要的功能,邮件合并在日常工作中还是有重要的应用。我们以制作一张高考的准考证为例,把整个过程展示给大家,希望您能...
  • 动态数据生成Word文档

    千次阅读 热门讨论 2014-09-06 11:59:39
    最近做项目,让做一个将页面上的数据按照固定的格式保存到一个Word中,记得当初在考试系统的时候,我们的那个系统也实现了这个功能,那个时候知识觉得,哇喔,好神奇啊,那个一个杂乱无章的页面,都可以让他显示的很...
  • 这两天一直在实现这样一个功能,即从数据库及其他数据源中获取相关数据,填充到一定的word模板中,然后将该模板下载下来(主要就是为了以简单的方式让老师看到他想看的系统中不同功能不同位置地方的数据) ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 60,831
精华内容 24,332
关键字:

word添加数据源