精华内容
下载资源
问答
  • 2019-04-20 16:17:22
                   

     

    数据库访问方式

     

    异构数据库系统混合查询—JDBC/ODBC

     

        异构数据库系统是由多个异构的成员数据库系统组成的数据库系统,异构性体现为各个成员数据库之间在硬件平台、操作系统或数据库管理系统等方面的不同。

             

        Internet上大量信息通过数据库系统可以得到有效的管理。由于历史等原因,Internet上的数据库系统不少是异构的。为了在Internet环境下,基于异种系统平台实现对异构数据库的查询和联合使用,必须提供一个独立于特定的数据库管理系统的统一编程界面。

     

         目前许多数据库系统均支持SQL,对于由SQL数据库组成的异构数据库系统,JDBC和ODBC为访问其异构成员提供了统一的方式,也为各异构成员之间的协作和多个成员之上的操作打下了基础。下文简单介绍了JDBC和ODBC对各个异构的数据库进行统一访问和协作的原理及其应用。

     

     

    ODBC


           ODBC(Open DataBase Connectivity)是微软倡导的、当前被业界广泛接受的、用于数据库访问的应用程序编程接口(API),它以X/Open和 ISO/IEC的调用级接口(CLI)规范为基础,并使用结构化查询语言(SQL)作为其数据库访问语言。 ODBC总体结构有四个组件:

        A. 应用程序:执行处理并调用ODBC API函数,以提交 SQL语句并检索结果。

        B. 驱动程序管理器(Driver Manager):根据应用程序需要加载/卸载驱动程序,处理ODBC函数调用,或把它们传送到驱动程序。

        C. 驱动程序:处理ODBC函数调用,提交SQL请求到一个指定的数据源,并把结果返回到应用程序。如果有必要,驱动程序修

                           改一应程序请求,以使请求与相关的DBMS支持的语法一致。

        D. 数据源:包括用户要访问的数据及其相关的操作系统、DBMS及用于访问DBMS的网络平台。

     

     

    ODBC驱动程序的使用把应用程序从具体的数据库调用中隔离开来。

    驱动程序管理器针对特定数据库的各个驱动程序进行集中管理,并向应用程序提供统一的标准接口,这就为ODBC的开放性奠定了基础。

     

     

    数据库独立性

         ODBC是为最大的互用性而设计的,要求一个应用程序有用相同的源代码(不用重新编译或重新链接)访问不同的数据库管理系统(DBMS)的能力。

     

         ODBC定义了一个标准的调用层接口(CLI)。这包含 X/Open和ISO/IEC的CLI规范中的所有函数,并提供应用程序普遍需要的附加函数。每个支持ODBC的DBMS需要不同的库或驱动程序,驱动程序实现ODBC API中的函数。当需要改变驱动程序时,应用程序不需要重新编译或者重新链接,只是动态加载新的驱动程序,并调用其中的函数即可。如果要同时访问多个DBMS系统,应用程序可加载多个驱动程序。如何支持驱动程序取决于操作系统,例如,在Windows操作系统上,驱动程序是动态链接库(DLL)。使用相同源代码的应用程序访问不同的DBMS,体现了 ODBC的数据库独立性。

     

     

    对数据库特殊功能的支持

        各个DBMS参照的标准、提供的功能不尽相同,应用程序如何以统一的接口使用各个DBMS特有的功能呢?

    一方面,ODBC为所有DBMS功能都定义了公共接口。这些DBMS功能比多数DBMS支持的更多,但只要求驱动程序实现这些功能的一个子集。另一方面,ODBC定义了API和SQL语法一致层,它规定驱动程序应支持的基本功能。 ODBC还提供两个函数(SQLGetInfo和SQLGetFunctions)返回关于驱动程序和DBMS能力的一般信息及驱动程序支持的函数列表。因此,应用程序可以检查DBMS支持的特殊功能。

     

        这样,编写应用程序时,就可以检查并自动使用各个驱动程序对应的DBMS所支持的特殊功能。这样做的优点是当增加DBMS支持的功能时,应用程序不需要改变,只需安装更新的驱动程序,应用程序便可以自动发现并使用这些功能。

     

    互操作能力

          通过使用多个驱动程序可以同时访问多个DBMS系统。 ODBC提供的Driver Manager实现所有的ODBC函数,多数是传递调用给驱动程序中的ODBC 函数,并静态链接应用程序,或在应用程序运行时加载它。这样,应用程序在Driver Manager 中按名调用驱动ODBC函数,而不是通过每个驱动程序中的指针。当应用程序需要通过特定的驱动程序时,它首先需要一个标识驱动程序的连接句柄。Driver Manager加载驱动程序,并存储每个驱动程序中的函数地址。要使驱动程序调用一个ODBC函数,可在应用程序调用 Driver Manager中的函数,并为驱动程序传送连接句柄,然后Driver Manager使用以前存储的地址来调用函数。

     

      ODBC可以同时连接到多个DBMS,解决了同时访问多个DBMS的问题,提供了异构成员数据库之间互操作的能力。

     

     

    JDBC


       JDBC(Java DataBase Connectivity)是Java与数据库的接口规范,JDBC定义了一个支持标准SQL功能的通用低层的应用程序编程接口(API),它由Java 语言编写的类和接口组成,旨在让各数据库开发商为Java程序员提供标准的数据库API。 JDBC API定义了若干Java中的类,表示数据库连接、SQL指令、结果集、数据库元数据等。它允许Java程序员发送SQL指令并处理结果。通过驱动程序管理器,JDBC API可利用不同的驱动程序连接不同的数据库系统。

     

          JDBC与ODBC都是基于X/Open的SQL调用级接口, JDBC的设计在思想上沿袭了ODBC,同时在其主要抽象和SQL CLI实现上也沿袭了ODBC,这使得JDBC容易被接受。JDBC的总体结构类似于ODBC,也有四个组件:应用程序、驱动程序管理器、驱动程序和数据源。

     

        JDBC保持了ODBC的基本特性,也独立于特定数据库。使用相同源代码的应用程序通过动态加载不同的JDBC驱动程序,可以访问不同的DBMS。连接不同的DBMS时,各个DBMS之间仅通过不同的URL进行标识。JDBC的 DatabaseMetaData接口提供了一系列方法,可以检查DBMS对特定特性的支持,并相应确定有什么特性,从而能对特定数据库的特性予以支持。与ODBC一样,JDBC也支持在应用程序中同时建立多个数据库连接,采用JDBC可以很容易地用SQL语句同时访问多个异构的数据库,为异构的数据库之间的互操作奠定基础。

     

         但是,JDBC除了具有ODBC的上述特点外,更具有对硬件平台、操作系统异构性的支持。这主要是因为ODBC使用的是C语言,而JDBC使用的是Java语言。Java语言具有与平台无关、移植性强、安全性高、稳定性好、分布式、面向对象等众多优点,而JDBC确保了“100%纯Java”的解决方案,利用Java的平台无关性, JDBC应用程序可以自然地实现跨平台特性,因而更适合于Internet上异构环境的数据库应用。

     

        此外,JDBC驱动程序管理器是内置的,驱动程序本身也可通过Web浏览器自动下载,无须安装、配置;而ODBC驱动程序管理器和ODBC驱动程序必须在每台客户机上分别安装、配置。

     

     

    JDBC和ODBC在Internet上的应用
     

    JDBC和ODBC由于具有数据库独立性甚至平台无关性,因而对Internet上异构数据库的访问提供了很好的支持。

     

         在Internet上访问数据库通常采用三层模式。以JDBC为例,在三层模式中客户端的Java Applet主要作为用户界面,它不直接与数据库交换信息,而是通过自定义的应用层网络协议与应用服务器交互,应用服务器通过JDBC与数据库服务器交换信息,并实现应用逻辑。DM3的JDBC和ODBC驱动程序支持目前流行的ASP和JSP技术,可以分别借助ODBC和JDBC同时访问Internet上多个异构的数据库。

     

         ASP是面向Web服务器的技术,客户端浏览器不需要任何附加的软件支持。ASP使用VBScript之类的脚本语言,它在HTML代码中嵌入某种程序代码,由HTML代码负责描述信息的显示样式,由嵌入的程序代码来描述处理逻辑。在ASP 下,VBScript代码被ASP引擎在Web服务器端解释执行,执行结果被重新嵌入到HTML代码中,然后一起发送给浏览器。这里,VBScript代码可以通过ODBC访问多个异构的数据库。

     

        JSP是一种基于Java Servlet的Web开发技术,它和ASP 非常相似,但又有区别:在JSP下,嵌入HTML页面的程序代码是Java代码;页面中嵌入的程序代码被编译成Servlet(这种编译操作仅在对JSP页面的第一次请求时发生)并由Java 虚拟机执行。这里Java代码可以通过JDBC访问多个异构的数据库,其平台无关性特别好。当前,Internet上的数据库应用已越来越多,JDBC和ODBC必将在Internet上的异构数据库访问中发挥重要的作用。

     

     

    微软了的数据访问方式历史演变

     

    ODBC(Open Database Connectivity):

    是Microsoft公司开发和定义的一套数据库访问标准,称为开放数据库系统互联。ODBC提供了一种编程接口,可以使用一个ODBC应用程序访问各种数据库管理系统,例如Access、MySQL、DB2、FoxPro、SQL Server和Oracle等,它是第一个使用SQL访问不同关系数据库的数据访问技术。使用ODBC应用程序能够通过单一的命令操纵不同的数据库,而开发人员需要做的仅仅只是针对不同的应用加入相应的ODBC驱动。
      
    DAO(Data  Access Objects):

    不像ODBC那样是面向C/C++程序员的,它是微软提供给Visual Basic开发人员的一种简单的数据访问方法,但不提供远程访问功能。


    RDO(Remote Data Object):

    在使用DAO访问不同的关系型数据库的时候,Jet引擎不得不在DAO和ODBC之间进行命令的转化,导致了性能的下降,而RDO(Remote Data Objects)的出现就顺理成章了。
      
    OLE DB(Object Linking and Embedding DataBase):

    OLE DB(对象链接和嵌入数据库)随着越来越多的数据以非关系型格式存储,需要一种新的架构来提供这种应用和数据源之间的无缝连接,基于COM(Component Object Model)的OLE DB应运而生了。
      
    ADO(ActiveX Data Object):

    基于OLE DB之上的ADO更简单、更高级、更适合Visual Basic程序员,同时消除了OLE DB的多种弊端,取而代之是微软技术发展的趋势。

     

     ADO.NET

    是一种基于标准的程序设计模型,可以用来创建分布式应用以实现数据共享。在ADO.NET中,DataSet占据重要地位,它是数据库里部分数据在内存中的拷贝。与ADO中的RecordSet不同,DataSet可以包括任意个数据表,每个数据表都可以用于表示自某个数据库表或视图的数据。DataSet驻留在内存中,且不与原数据库相连,即无需与原数据库保持连接。完成工作的底层技术是XML,它是DataSet所采用的存储和传输格式。在运行期间,组件 (如某个业务逻辑对象或asp.net web表单)之间需要交换DataSet中的数据。数据以XML文件的形式从一个组件传输给另一个组件,由接收组件将文件还原为DataSet形式。DataSet的有关方法与关系数据模型完全一样

     

         因为各个数据源的协议各不相同,我们需要通过正确的协议来访问数据源。有些比较老的数据源用ODBC协议,其后的一些数据源用OleDb协议,现在,仍然还有许多新的数据源在不断出现,ADO.NET提供了访问数据源的公共方法,对于不同的数据源,它采用不同的类库。这些类库称为Data Providers,并且通常是以数据源的类型以及协议来命名的

     

    ADO.NET Data Providers是一组提供访问指定数据源的基本类库。API的开头字符表明了他们支持的协议。

     

    Provider

    API 前缀

    数据源描述

    ODBC Data Provider

    Odbc

    提供ODBC接口的数据源。一般是比较老的数据库。

    OleDb Data Provider

    OleDb

    提供OleDb接口的数据源,比如AccessExcel

    Oracle Data Provider

    Oracle

    Oracle数据库

    SQL Data Provider

    Sql

    Microsoft SQL Server数据库

    Borland Data Provider

    Bdp

    通用的访问方式能访问许多数据库,比如InterbaseSQL ServerIBM DB2Oracle

     

     

     

    这些数据库连接方式ODBC,DAO,RDO,OLE DB,ADO,ADO.NET都是基于oracle客户端(oracle OCI),中间通过SQL*Net与数据库通信。如果为了追求性能,可以自己开发最适合自己数据库连接方式。

     

     

     

    Java程序连接数据库的方式

     

    Java程序连接数据库的方式有三种方式:OCI方式、thin方式和JdbcOdbc桥方式

     

     

    1. OCI方式

    这种实现方法是直接使用数据库厂商提供的用专用的网络协议创建的驱动程序,通过它可以直接将JDBC API调用转换为直接网络调用。这种调用方式一般性能比较好,而且也是实用中最简单的方法。因为它不需要安装其他的库或中间件。几乎所有的数据库厂商都为他们的数据库提供了这种数据库提供了这种JDBC驱动程序,也可以从第三方厂商获得这些驱动程序。

     

    应用程序---JDBC API---驱动程序---数据源

     

     

    2. Thin方式

    thin方式是纯java实现tcp/ip的通讯,而oci方式,客户端通过native java method调用c library访问服务端,而这个c library就是oci(oracle called interface),因此这个oci总是需要随着oracle客户端安装

     

    oracle jdbc oci方式是用java与c两种语言编写的,把jdbc call调用,转换成c调用,通过SQL*Net与数据库通信。而oracle jdbc thin驱动方式全采用java编写,使用jvm统一管理内存,也是通过SQL*Net与数据库通信。

     

     

    3. JdbcOdbc桥方式(用于windows平台)

    JDBC-ODBC桥接器是用JdbcOdbc.Class和一个用于访问ODBC驱动程序的本地库实现的。

    由于JDBC在设计上与ODBC很接近。在内部,这个驱动程序把JDBC的方法映射到ODBC调用上,这样,JDBC就可以和任何可用的ODBC驱动程序进行交互了。这种桥接器的优点是,它使JDBC目前有能力访问几乎所有的数据库。通行方式如下所示:

     

    应用程序--->JDBC API--->JDBC-ODBC--->ODBC API--->ODBC层--->数据源

     

     

     

     

    事务控制

    无论是java,还是.net都是支持事务控制的,事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问

     

    事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)的缩写。

     

    原子性:表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。

    一致性:表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。

    隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。

    持久性:表示已提交的数据在事务执行失败时,数据的状态都应该正确。

     

     

    Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。

    .net事务分为本地事务和分布式事务

     

     

     

    jdbc/odbc主要优化方式:


         1.选择正确的jdbc/odbc驱动程序
         2.Connention的优化  使用连接池来管理Connection对象
         3.Statement的优化   使用批量更新等
         4.Result的优化  正确的从数据库中get数据等

     

     

     

     

    -------end------

     

     

     

     

     

     

               
    更多相关内容
  • 如何通过Spring Boot配置动态数据源访问多个数据库

    万次阅读 多人点赞 2018-03-18 14:59:52
    之前写过一篇博客《Spring+Mybatis+Mysql搭建分布式数据库访问框架》描述如何通过Spring+Mybatis配置动态数据源访问多个数据库。但是之前的方案有一些限制(原博客中也描述了):只适用于数据库数量不多且固定的情况。...

    之前写过一篇博客《Spring+Mybatis+Mysql搭建分布式数据库访问框架》描述如何通过Spring+Mybatis配置动态数据源访问多个数据库。但是之前的方案有一些限制(原博客中也描述了):只适用于数据库数量不多且固定的情况。针对数据库动态增加的情况无能为力。

    下面讲的方案能支持数据库动态增删,数量不限。

    一、数据库环境准备

    下面以Mysql为例,先在本地建3个数据库用于测试。需要说明的是本方案不限数据库数量,支持不同的数据库部署在不同的服务器上。如图所示db_project_001、db_project_002、db_project_003。
    这里写图片描述

    #二、搭建Java后台微服务项目
    创建一个Spring Boot的maven项目:
    这里写图片描述

    config:数据源配置。
    datasource:自己实现的动态数据源相关类。
    dbmgr:管理项目编码与数据库IP、名称的映射关系(实际项目中这部分数据保存在redis缓存中,可动态增删)。
    mapper:mybatis的数据库访问接口。
    model:映射模型。
    rest:微服务对外发布的restful接口,这里用来测试。
    application.yml:配置数据库JDBC参数。

    三、详细的代码实现

    1、数据源配置管理类(DataSourceConfig.java)

    package com.elon.dds.config;
    
    import javax.sql.DataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import com.elon.dds.datasource.DynamicDataSource;
    
    /**
     * 数据源配置管理。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    @Configuration
    @MapperScan(basePackages="com.elon.dds.mapper", value="sqlSessionFactory")
    public class DataSourceConfig {
    
    	/**
    	 * 根据配置参数创建数据源。使用派生的子类。
    	 * 
    	 * @return 数据源
    	 */
    	@Bean(name="dataSource")
    	@ConfigurationProperties(prefix="spring.datasource")
    	public DataSource getDataSource() {
    		DataSourceBuilder builder = DataSourceBuilder.create();
    		builder.type(DynamicDataSource.class);
    		return builder.build();
    	}
    	
    	/**
    	 * 创建会话工厂。
    	 * 
    	 * @param dataSource 数据源
    	 * @return 会话工厂
    	 */
    	@Bean(name="sqlSessionFactory")
    	public SqlSessionFactory getSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) {
    		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    		bean.setDataSource(dataSource);
    		
    		try {
    			return bean.getObject();
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    }
    
    

    2、定义动态数据源

    (1)首先增加一个数据库标识类,用于区分不同的数据库(DBIdentifier.java)

    由于我们为不同的project创建了单独的数据库,所以使用项目编码作为数据库的索引。而微服务支持多线程并发的,采用线程变量。

    package com.elon.dds.datasource;
    
    /**
     * 数据库标识管理类。用于区分数据源连接的不同数据库。
     * 
     * @author elon
     * @version 2018-02-25
     */
    public class DBIdentifier {
    	
    	/**
    	 * 用不同的工程编码来区分数据库
    	 */
    	private static ThreadLocal<String> projectCode = new ThreadLocal<String>();
    
    	public static String getProjectCode() {
    		return projectCode.get();
    	}
    
    	public static void setProjectCode(String code) {
    		projectCode.set(code);
    	}
    }
    
    

    (2)从DataSource派生了一个DynamicDataSource,在其中实现数据库连接的动态切换(DynamicDataSource.java)

    package com.elon.dds.datasource;
    
    import java.lang.reflect.Field;
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.apache.tomcat.jdbc.pool.DataSource;
    import org.apache.tomcat.jdbc.pool.PoolProperties;
    
    import com.elon.dds.dbmgr.ProjectDBMgr;
    
    /**
     * 定义动态数据源派生类。从基础的DataSource派生,动态性自己实现。
     * 
     * @author elon
     * @version 2018-02-25
     */
    public class DynamicDataSource extends DataSource {
    	
    	private static Logger log = LogManager.getLogger(DynamicDataSource.class);
    	
    	/**
    	 * 改写本方法是为了在请求不同工程的数据时去连接不同的数据库。
    	 */
    	@Override
    	public Connection getConnection(){
    		
    		String projectCode = DBIdentifier.getProjectCode();
    		
    		//1、获取数据源
    		DataSource dds = DDSHolder.instance().getDDS(projectCode);
    		
    		//2、如果数据源不存在则创建
    		if (dds == null) {
    			try {
    				DataSource newDDS = initDDS(projectCode);
    				DDSHolder.instance().addDDS(projectCode, newDDS);
    			} catch (IllegalArgumentException | IllegalAccessException e) {
    				log.error("Init data source fail. projectCode:" + projectCode);
    				return null;
    			}
    		}
    		
    		dds = DDSHolder.instance().getDDS(projectCode);
    		try {
    			return dds.getConnection();
    		} catch (SQLException e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    	
    	/**
    	 * 以当前数据对象作为模板复制一份。
    	 * 
    	 * @return dds
    	 * @throws IllegalAccessException 
    	 * @throws IllegalArgumentException 
    	 */
    	private DataSource initDDS(String projectCode) throws IllegalArgumentException, IllegalAccessException {
    		
    		DataSource dds = new DataSource();
    		
    		// 2、复制PoolConfiguration的属性
    		PoolProperties property = new PoolProperties();
    		Field[] pfields = PoolProperties.class.getDeclaredFields();
    		for (Field f : pfields) {
    			f.setAccessible(true);
    			Object value = f.get(this.getPoolProperties());
    			
    			try
    			{
    				f.set(property, value);				
    			}
    			catch (Exception e)
    			{
    				//有一些static final的属性不能修改。忽略。
    				log.info("Set value fail. attr name:" + f.getName());
    				continue;
    			}
    		}
    		dds.setPoolProperties(property);
    
    		// 3、设置数据库名称和IP(一般来说,端口和用户名、密码都是统一固定的)
    		String urlFormat = this.getUrl();
    		String url = String.format(urlFormat, ProjectDBMgr.instance().getDBIP(projectCode), 
    				ProjectDBMgr.instance().getDBName(projectCode));
    		dds.setUrl(url);
    
    		return dds;
    	}
    }
    
    

    (3)通过DDSTimer控制数据连接释放(DDSTimer.java)

    package com.elon.dds.datasource;
    
    import org.apache.tomcat.jdbc.pool.DataSource;
    
    /**
     * 动态数据源定时器管理。长时间无访问的数据库连接关闭。
     * 
     * @author elon
     * @version 2018年2月25日
     */
    public class DDSTimer {
    	
    	/**
    	 * 空闲时间周期。超过这个时长没有访问的数据库连接将被释放。默认为10分钟。
    	 */
    	private static long idlePeriodTime = 10 * 60 * 1000;
    	
    	/**
    	 * 动态数据源
    	 */
    	private DataSource dds;
    	
    	/**
    	 * 上一次访问的时间
    	 */
    	private long lastUseTime;
    	
    	public DDSTimer(DataSource dds) {
    		this.dds = dds;
    		this.lastUseTime = System.currentTimeMillis();
    	}
    	
    	/**
    	 * 更新最近访问时间
    	 */
    	public void refreshTime() {
    		lastUseTime = System.currentTimeMillis();
    	}
    	
    	/**
    	 * 检测数据连接是否超时关闭。
    	 * 
    	 * @return true-已超时关闭; false-未超时
    	 */
    	public boolean checkAndClose() {
    		
    		if (System.currentTimeMillis() - lastUseTime > idlePeriodTime)
    		{
    			dds.close();
    			return true;
    		}
    		
    		return false;
    	}
    
    	public DataSource getDds() {
    		return dds;
    	}
    }
    
    

    (4)通过DDSHolder来管理不同的数据源,提供数据源的添加、查询功能(DDSHolder.java)

    package com.elon.dds.datasource;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Timer;
    
    import org.apache.tomcat.jdbc.pool.DataSource;
    
    /**
     * 动态数据源管理器。
     * 
     * @author elon
     * @version 2018年2月25日
     */
    public class DDSHolder {
    	
    	/**
    	 * 管理动态数据源列表。<工程编码,数据源>
    	 */
    	private Map<String, DDSTimer> ddsMap = new HashMap<String, DDSTimer>();
    
    	/**
    	 * 通过定时任务周期性清除不使用的数据源
    	 */
    	private static Timer clearIdleTask = new Timer();
    	static {
    		clearIdleTask.schedule(new ClearIdleTimerTask(), 5000, 60 * 1000);
    	};
    	
    	private DDSHolder() {
    		
    	}
    	
    	/*
    	 * 获取单例对象
    	 */
    	public static DDSHolder instance() {
    		return DDSHolderBuilder.instance;
    	}
    	
    	/**
    	 * 添加动态数据源。
    	 * 
    	 * @param projectCode 项目编码 
    	 * @param dds dds
    	 */
    	public synchronized void addDDS(String projectCode, DataSource dds) {
    		
    		DDSTimer ddst = new DDSTimer(dds);
    		ddsMap.put(projectCode, ddst);
    	}
    	
    	/**
    	 * 查询动态数据源
    	 * 
    	 * @param projectCode 项目编码
    	 * @return dds
    	 */
    	public synchronized DataSource getDDS(String projectCode) {
    		
    		if (ddsMap.containsKey(projectCode)) {
    			DDSTimer ddst = ddsMap.get(projectCode);
    			ddst.refreshTime();
    			return ddst.getDds();
    		}
    		
    		return null;
    	}
    	
    	/**
    	 * 清除超时无人使用的数据源。
    	 */
    	public synchronized void clearIdleDDS() {
    		
    		Iterator<Entry<String, DDSTimer>> iter = ddsMap.entrySet().iterator();
    		for (; iter.hasNext(); ) {
    			
    			Entry<String, DDSTimer> entry = iter.next();
    			if (entry.getValue().checkAndClose())
    			{
    				iter.remove();
    			}
    		}
    	}
    	
    	/**
    	 * 单例构件类
    	 * @author elon
    	 * @version 2018年2月26日
    	 */
    	private static class DDSHolderBuilder {
    		private static DDSHolder instance = new DDSHolder();
    	}
    }
    
    

    (5)定时器任务ClearIdleTimerTask用于定时清除空闲的数据源(ClearIdleTimerTask.java)

    package com.elon.dds.datasource;
    
    import java.util.TimerTask;
    
    /**
     * 清除空闲连接任务。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    public class ClearIdleTimerTask extends TimerTask {
    	
    	@Override
    	public void run() {
    		DDSHolder.instance().clearIdleDDS();
    	}
    }
    
    

    (6)管理项目编码与数据库IP和名称的映射关系(ProjectDBMgr.java)

    package com.elon.dds.dbmgr;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * 项目数据库管理。提供根据项目编码查询数据库名称和IP的接口。
     * @author elon
     * @version 2018年2月25日
     */
    public class ProjectDBMgr {
    	
    	/**
    	 * 保存项目编码与数据名称的映射关系。这里是硬编码,实际开发中这个关系数据可以保存到redis缓存中;
    	 * 新增一个项目或者删除一个项目只需要更新缓存。到时这个类的接口只需要修改为从缓存拿数据。
    	 */
    	private Map<String, String> dbNameMap = new HashMap<String, String>();
    	
    	/**
    	 * 保存项目编码与数据库IP的映射关系。
    	 */
    	private Map<String, String> dbIPMap = new HashMap<String, String>();
    	
    	private ProjectDBMgr() {
    		dbNameMap.put("project_001", "db_project_001");
    		dbNameMap.put("project_002", "db_project_002");
    		dbNameMap.put("project_003", "db_project_003");
    		
    		dbIPMap.put("project_001", "127.0.0.1");
    		dbIPMap.put("project_002", "127.0.0.1");
    		dbIPMap.put("project_003", "127.0.0.1");
    	}
    	
    	public static ProjectDBMgr instance() {
    		return ProjectDBMgrBuilder.instance;
    	}
    	
    	// 实际开发中改为从缓存获取
    	public String getDBName(String projectCode) {
    		if (dbNameMap.containsKey(projectCode)) {
    			return dbNameMap.get(projectCode);
    		}
    		
    		return "";
    	}
    	
    	//实际开发中改为从缓存中获取
    	public String getDBIP(String projectCode) {
    		if (dbIPMap.containsKey(projectCode)) {
    			return dbIPMap.get(projectCode);
    		}
    		
    		return "";
    	}
    	
    	private static class ProjectDBMgrBuilder {
    		private static ProjectDBMgr instance = new ProjectDBMgr();
    	}
    }
    
    

    (7)编写数据库访问的mapper(UserMapper.java)

    package com.elon.dds.mapper;
    
    import java.util.List;
    
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Result;
    import org.apache.ibatis.annotations.Results;
    import org.apache.ibatis.annotations.Select;
    
    import com.elon.dds.model.User;
    
    /**
     * Mybatis映射接口定义。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    @Mapper
    public interface UserMapper
    {
    	/**
    	 * 查询所有用户数据
    	 * @return 用户数据列表
    	 */
    	@Results(value= {
    			@Result(property="userId", column="id"),
    			@Result(property="name", column="name"),
    			@Result(property="age", column="age")
    	})
    	@Select("select id, name, age from tbl_user")
    	List<User> getUsers();
    } 
    
    

    (8)定义查询对象模型(User.java)

    package com.elon.dds.model;
    
    public class User
    {
    	private int userId = -1;
    
    	private String name = "";
    	
    	private int age = -1;
    	
    	@Override
    	public String toString()
    	{
    		return "name:" + name + "|age:" + age;
    	}
    
    	public int getUserId()
    	{
    		return userId;
    	}
    
    	public void setUserId(int userId)
    	{
    		this.userId = userId;
    	}
    
    	public String getName()
    	{
    		return name;
    	}
    
    	public void setName(String name)
    	{
    		this.name = name;
    	}
    
    	public int getAge()
    	{
    		return age;
    	}
    
    	public void setAge(int age)
    	{
    		this.age = age;
    	}
    }
    
    

    (9)定义查询数据的restful接口(WSUser.java)

    package com.elon.dds.rest;
    
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.elon.dds.datasource.DBIdentifier;
    import com.elon.dds.mapper.UserMapper;
    import com.elon.dds.model.User;
    
    /**
     * 用户数据访问接口。
     * 
     * @author elon
     * @version 2018年2月26日
     */
    @RestController
    @RequestMapping(value="/user")
    public class WSUser {
    
    	@Autowired
    	private UserMapper userMapper;
    	
    	/**
    	 * 查询项目中所有用户信息
    	 * 
    	 * @param projectCode 项目编码
    	 * @return 用户列表
    	 */
    	@RequestMapping(value="/v1/users", method=RequestMethod.GET)
    	public List<User> queryUser(@RequestParam(value="projectCode", required=true) String projectCode) 
    	{
    		DBIdentifier.setProjectCode(projectCode);
    		return userMapper.getUsers();
    	}
    }
    
    

    要求每次查询都要带上projectCode参数。

    (10)编写Spring Boot App的启动代码(App.java)

    package com.elon.dds;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    /**
     * Hello world!
     *
     */
    @SpringBootApplication
    public class App 
    {
        public static void main( String[] args )
        {
            System.out.println( "Hello World!" );
            SpringApplication.run(App.class, args);
        }
    }
    
    

    四、在application.yml中配置数据源

    其中的数据库IP和数据库名称使用%s。在执行数据操作时动态切换。

    spring:
     datasource:
      url: jdbc:mysql://%s:3306/%s?useUnicode=true&characterEncoding=utf-8
      username: root
      password: 
      driver-class-name: com.mysql.jdbc.Driver
    
    logging:
     config: classpath:log4j2.xml
    

    五、测试方案

    1、查询project_001的数据,正常返回

    这里写图片描述

    2、查询project_002的数据,正常返回

    这里写图片描述

    代码在github上的路径:https://github.com/ylforever/elon-dynamicdatasource

    展开全文
  • sql server 怎么实现建立一个用户只能看到一个数据库
  • 详解-程序是如何访问数据库的?

    千次阅读 2020-04-15 21:45:10
    应用访问数据库的过程,通过一层一层的抽象,将最初的通过TCP连接传输复杂的协议数据来操作数据,封装成我们现在的简单几配置就能操作数据库中数据,真是太神奇了,下面,我们就从这一层一层抽象入手,看看每一层...

    ​ ​ 最近我们项目中引入了时序数据库TDengine,所以可以访问的数据库需要配置两个,并且在不同的时机访问不同的数据库,在配置多数据源的过程中,我想了很多问题,花费了一部分精力将数据库访问的原理彻彻底底的梳理了一下,在这里我想和大家分享一下。

    ​ ​ 在计算机程序中,没有什么问题是通过增加一层抽象层解决不了的,如果有,那就再增加一层。

    ​ ​ 应用访问数据库程序也一样,通过一层一层的抽象,将最初的通过TCP连接传输复杂的协议数据来操作数据,封装成我们现在的简单几个配置就能操作数据库中数据,真是太神奇了,下面,我们就从这一层一层抽象入手,看看每一层抽象都做了什么,为什么要这样做?

    ​ ​ 下面,我们主要从以下几个方面聊聊:
    ​ ​ 1.如何访问数据库?
     ​ 2.为什么要有JDBC规范,JDBC规范定义了什么?
    ​ ​ 3.DataSource和DriverManager有什么关系,DataSource本质是什么,DataSource连接池怎么实现?
    ​ ​ 4.spring的JdbcTemplate又干了什么?
    ​ ​ 5.mybatis的本质和逻辑是什么?
    ​ ​ 6.mybatis和springboot是怎么结合的?如何配置多数据源?


     

    1.如何访问数据库?

    ​ ​ 众所周知,两个进程之间如果要通过网络进行连接并且通行,那么最简单也是最通用的就是socket编程,socket客户端服务端和客户端通过tcp连接起来,就可以双向通信了。

    ​ ​ 所以最初我们应用程序访问数据库的时候,就是建立一个tcp连接,然后应用程序和数据库共同商量一个应用层协议,进行数据库的操作。
    ​ ​ 这个应用层协议是很复杂的,需要定义很多东西,比如:
    ​ ​ 应用程序想要和数据库建立连接的时候,发送什么消息来握手?
    ​ ​ 怎么去做安全认证,授权,数据加密?
    ​ ​ 要查询数据的时候发送什么?数据库怎么给我返回?
    ​ ​ 增删改数据的时候发送什么?数据库怎么告知我结果?
    ​ ​ 还有数据库相关的事务,函数什么的怎么处理?怎么处理分包、粘包问题?

    ​ ​ 所以说,如果使用这种方式进行编程会很复杂,但是,这种方式确实解决了应用程序访问数据库的问题。原理如下图所示:

    img

    2.为什么要有JDBC规范,JDBC规范定义了什么?

     ​ 后来,越来越多的数据库定义了自己的业务协议,如果要访问这些数据库操作数据的话,就需要按照他们的协议来,这样,如果我们的程序要连接两个数据库,就需要写两套访问数据库的代码,并且如果后面切换数据库的时候,所有和数据库相关的代码都会被推翻重写,这是很不合理的。

    ​ ​ 后来,java也觉得这样实在不是办法,一定要想办法解决。

    ​ ​ 首先,对所有数据库的访问在代码中一定要统一起来才能减少我们开发人员的负担,那么怎么解决协议不一致的问题呢?我们程序语言总不能规范人家数据库的通信协议吧,所以我们必须有一个抽象层,将这些繁杂的访问细节屏蔽了,提供给我们开发人员一个更加简洁的接口进行数据库的操作,于是定义了JDBC(java database connection)规范。

    ​ ​ JDBC规范主要是将我们应用程序可能用到的数据库操作定义成统一的接口,我们不管使用什么数据库,只需要调用这些统一的接口就可以,比如获取连接、增删改查数据接口等等,至于接口的具体实现,由于每个数据库的交互协议都不相同,所以由数据库厂商来提供实现,我们只需要引入一个驱动包就行。

    ​ ​ 那么这个JDBC规范中,要包含哪些东西呢?

    ​ ​ 首先,要访问数据库,那就必须有一个连接,定义一个Connection吧;然后要执行数据库的sql怎么办?总不能用连接执行sql吧,因此需要定义一个对象来执行sql,就定义一个Statement吧,'陈述’一下,访问数据库要干啥;执行了sql怎么代表包装返回结果呢?定义一个结果集ResultSet吧;因此,应用层代码应该这样写:

    img

    ​ ​ 先获取连接Connection,从连接获取可以执行sql的Statement,然后执行sql,返回结果为ResultSet,从结果集中,我们可以获取返回的一切,一气呵成,完美!

    ​ ​ 那么还有一个问题,怎么获取数据库的连接呢?为了遵守开闭原则,我们不能直接new数据库的连接实现,我们可以使用工厂模式,用工厂来创建Connection,这样就解耦了连接使用者和连接创建者,如果我们连接实现改变了,我也不用改变连接的使用者。这个工厂我们就叫他Driver吧。

    ​ ​ 相对于不同的数据库,我们会有不同的Driver,因此我们需要一个DriverManager去管理这些Driver。
    ​ ​ 这样,获取连接的过程如下(以mysql为例):

    ​ ​ 1.我们将mysql的Driver加载进来。

    img

    ​ ​ 2.在Mysql Driver的静态代码块中,会将Driver注册到DriverManager中,源代码如下:

    img

    ​ ​ 3.这样我们就可以在DriverManager中,通过不同的url获取不同的驱动,从而获取不同的连接了,代码如下:

    img

    ​ ​ 在getConnection方法中,会遍历所有的Driver实现,判断url是哪个Driver管理,比如mysqlDriver通过"jdbc:mysql:"判断出是自己管理,因此创建一个mysqlConnection,并返回,这样我们就获得了自己想要的连接了。

    ​ ​ 综合起来,获取连接就这几行代码:

    img

    ​ ​ ok,到这里,jdbc就诞生了!

    3.DataSource和DriverManager有什么关系,DataSource本质是什么,DataSource连接池怎么实现?

    ​ ​ 我们在上面的JDBC规范中可以看到连接可以通过DriverManager获取,但是我们在编码中接触的获取连接的东西都是DataSource,那么这两个有什么关系呢?DataSource到底是什么?

    ​ ​ 在JDBC规范中,我们可以通过DriverManager获取Connection,但是在getConncetion方法的入参中需要传入很多配置参数,这些配置参数是数据库相关的东西,和我们的业务逻辑没有关系,所以说并不应该在我们代码中出现,我们需要将这些配置参数和应用程序解耦。

    ​ ​ 那么怎么做呢?我们可以将这些配置参数和本身的数据库驱动封装起来,代表一个数据源,并且我们程序可以通过JNDI配置这些参数。这个封装起来的东西,就是DataSource。(JNDI全称java naming and directory interface,是一个java定义的规范,也是所有的数据库厂商需要实现的接口,简单解释就是给资源起个名字,然后通过名字找资源,这样就解耦了资源使用者和资源提供着,在数据源中的表现就是:我们不用在代码中关心数据库的配置,我们只需要知道dataSource的名字,然后系统就可以帮我们自动找到数据库的配置,进行数据的操作)。

    ​ ​ 现在,我们只要知道DataSource的名称就可以直接获取Conncection,而那些配置参数都在底层通过jndi查找,这样,我们程序中就和数据库配置解耦合了。当然,万变不离其宗,DataSource底层还是使用DriverManager获取Connection。

    ​ ​ 在使用DataSource的过程中,我们渐渐的发现了一个问题,就是每次执行一个语句就必须重新建立一个Connection,然后又进行三次握手,简直太浪费资源了,于是,java提出了连接池的概念:连接池就是可以缓存连接的地方。我们在启动或者连接不够时建立一些连接, 放到连接池里, 需要连接时直接去取就可以了,用完了之后再放回去就好了,这样就实现了连接的复用,节省了资源。另外,连接池还可以加快操作数据库的速度(获取连接速度加快),限制数据库连接的数量等等。

    ​ ​ 但是,怎么能让用户无感知使用连接池呢?也就是说,用户在使用连接池和不使用连接池是同一套代码?JDBC在DataSource的实现中,增加了连接池的实现,在获取连接的时候,从连接池的DataSource实现中获取的连接就是池化的连接,这个链接close的时候并不是真正的关闭,而是release到连接池中了,比如HikariDataSource:

    public class HikariDataSource extends HikariConfig implements DataSource, Closeable{}
    

    4.spring的JdbcTemplate又干了什么?

    ​ ​ 我们上面已经基本把jdbc简单说完了,那么现在的代码成什么样子了呢?如下所示:

    img

    ​ ​ 大家有没有发现什么?

    ​ ​ 这些代码也太复杂了吧,我就是想查询个数据而已,写了这么一大堆代码,而且这其中有很多代码都是可以通用的,这时候,作为抽象界的大师,spring出来发话了:

    ​ ​  我们现在的代码流程是这样的:

    ​ ​  声明配置参数-获取连接-获取Statement-执行sql-处理返回值-关闭Statement-关闭连接(外加处理异常以及事务等等)

    ​ ​ 但是我们关心的其实只有上面的黑体部分,也就是我们通过配置参数告诉在哪里查,然后告诉sql语句,你把返回值查出来我处理一下就好了,就不会有那么冗余的代码了。”

    ​ ​ 所以,spring推出了JdbcTemplate,它的代码使用方式如下:

    img

    ​ ​ 这个jdbcTemplate可以通过如下方式获得:

    img

    ​ ​ 当然,按照spring的编码风格来说,JdbcTemplate应该是被‘注入’进去的。

    5.mybatis的本质和逻辑是什么?

    ​ ​  聪明的程序员使用JDBC或者spring的JdbcTemplate进行数据库的操作持续了很久时间,后来,大家越来越觉得别扭:我使用一个面向对象的开发语言,为什么里面要写和我的业务逻辑没有密切关系的数据库sql?为什么我还必须要知道数据库的字段名称是什么样的?为什么我的sql或者数据库字段名称变了还得重新编译我的应用程序?

    ​ ​ 大家渐渐明白了,应用程序和数据库的关系断的还不够彻底,还需要继续解耦。

    ​ ​ 这个时候,mybatis出来了,它几乎完美的解决了这个问题,那么它是怎么做的呢?

    ​ ​ 首先我们要解耦的话,就要找到一种简便的方式,通过配置文件或者注解将数据库的数据和程序中的对象映射起来,我们只需要操作我们的对象,就相当于操作了数据库中的数据。

    ​ ​ 也就是说,我们可以将这些数据库相关的东西,比如说是数据库的端口,ip,连接配置,数据库的sql等写在配置文件或者注解中,然后将这些配置文件在适当的时候转换成jdbc代码,代替程序去访问数据库,并将结果通过配置自动解析成我们所需要的结果。

    ​ ​ 当然,做什么都要站在巨人的肩膀上,mybatis的本质仍然是在jdbc的基础上做了封装,通过极其简单的xml配置或者注解来配置和映射原生类型、接口和java的POJO为数据库中的记录。他几乎避免了所有的jdbc代码和手动设置参数以及获取结果集。

    ​ ​ 为了做到这些,mybatis定义了以下几个概念:

    ​ ​ SqlSessionFactoryBuilder SqlSessionFactory SqlSession

    ​ ​ SqlSession可以直接执行sql语句,执行数据库回滚提交等操作(这个底层仍然是调用的jdbc的执行sql的方法,至于为什么可以强制转换成Blog对象,我们下面再说),比如:

    Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
    

    ​ ​ SqlSessionFactory是用来创建SqlSession的(使用工厂模式是因为可以依赖倒置,符合开闭原则),他可以传入一些和session绑定的属性 比如是否会开启一个事务 是否自定义连接等等。

    SqlSession session = sqlSessionFactory.openSession();
    

    ​ ​ SqlSessionFactoryBuilder是用来创建sqlSessionFactory的,主要的作用是从外部资源(比如xml properties)获取配置,创建factory,他配置的是一些通用的参数。

    String resource = "org/mybatis/builder/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
    SqlSessionFactory factory = builder.build(inputStream);
    

    ​ ​ 注:当 Mybatis 与一些依赖注入框架(如 Spring)同时使用时,SqlSession 将被依赖注入框架所创建。

    ​ ​ mybatis-config.xml的格式见附录。

    ​ ​ 通过SqlSessionFactoryBuilder以及SqlSessionFactory,我们可以将访问数据库需要的配置到添加到SqlSession中去,SqlSession才能更好的去操作数据库。

    ​ ​ 其中,SqlSessionFactoryBuilder以及SqlSessionFactory解耦了访问数据库的配置信息,和jndi的作用差不多,SqlSession解耦了sql语句以及数据库字段。

    ​ ​ 下面重头戏就来了,我们分析一下,SqlSession是怎么解耦sql语句以及数据库字段的。

    ​ ​ 在前面我们说了,我们可以使用

    Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
    

    ​ ​ 这样的方式来执行一个操作,我们发现它并没有sql语句或者数据库字段,其实它是把数据库相关的东西解耦了,那么它做了什么呢?

    ​ ​ 首先,为了解耦sql,我们会将sql配置到xml中(也可以通过注解,但是由于注解方式不能解决复杂的sql,因此这里就不说了),类似如下:

    <?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="org.mybatis.example.BlogMapper">
      <select id="selectBlog" resultType="Blog">
        select * from Blog where id = #{id}
      </select>
    </mapper>
    

    ​ ​ 并且这些sql语句在启动的时候会通过namespace+id作为key来注册到mapper中心。

    ​ ​ 这样,在执行selectOne的时候我们就可以通过第一个参数"org.mybatis.example.BlogMapper.selectBlog"去mapper中心查找,就可以找到对应的sql语句以及结果映射方式resultType或者resultMap。

    ​ ​ 到这里我们就可以把selectOne方法的底层逻辑大概说清楚了,基本上就是jdbc那一套,声明配置参数-获取连接-获取Statement-执行sql-处理返回值-关闭Statement-关闭连接,只不过在执行sql的时候sql语句去xml中获取了,然后在处理结果的时候,也在xml中获取了而已。

    ​ ​ 其实到这里为止,mybatis的目标已经实现了:找一种简便的方式,通过配置文件或者注解将数据库的数据和程序中的对象映射起来,我们只需要操作我们的对象,就相当于操作了数据库中的数据。

    ​ ​ 但是mybatis还不满足于此,它仍然觉得手写字符串类型的“nameSpace+id”是比较麻烦而且容易出错的,也就是说,想把selectOne方法的第一个参数给去掉。

    ​ ​ 所以,mybatis 还提供了一种执行操作的方式,类似于这样:

    try (SqlSession session = sqlSessionFactory.openSession()) {
      BlogMapper mapper = session.getMapper(BlogMapper.class);
      Blog blog = mapper.selectBlog(101);
    }
    

    ​ ​ 这个的逻辑基本上是说,我创建一个接口,我的接口全限定名和xml配置的nameSpace保持一致,然后接口方法名称和xml配置的id保持一致,这样,我就可以通过接口方法的全限定名来在xml中找到自己对应的sql。这个一对一的假定逻辑也就提供了生成接口实现的可能,大概实现的方法是这样的:

    public Blog selectBlog(long id){
      return (Blog) session.selectOne("{方法全限定名}", id);
    }
    

    ​ ​ 所以,在mybatis中,我们只要能够获取session,那就可以通过session获取接口实现,就可以执行所有的访问数据库的方法了。

    6.mybatis和springboot是怎么结合的?如何配置多数据源?

    ​ ​ 首先,sqlsessionFactoryBuilder sqlsessionFactory sqlsession的创建都被封装了起来,只要按照spring或者springboot提供的方式去写配置文件,我们就可以配置session。

    ​ ​ 因为springboot使用的是依赖注入的方式,所以我们希望在访问数据库的地方可以直接注入Mapper接口的实现。

    ​ ​ 第一步,我们需要知道哪些接口是需要mybatis生成实现的,springboot通过@MapperScanner配置。

    ​ ​ 然后,在启动的时候这些扫描到的Mapper接口会依据mapper.xml动态生成接口的实现,一般可以通过“BlogMapper mapper = session.getMapper(BlogMapper.class)”这种方式来生成实现。

    ​ ​ 最后将这些生成的实现纳入到上下文的管理中,就可以直接注入了。

    ​ ​ 因为大多数的应用程序都使用的是单数据源,所以springboot对单数据源的配置做了很好的封装,只要在application.properties中配置几个参数就可以了,如下:

    ## mybatis配置
    mybatis.mapper-locations=classpath*:/mapper/**/*.xml
    mybatis.config-location=classpath:/config/mybatis-config.xml
    
    
    ## 连接池配置
    ## 数据库地址
    spring.datasource.url=jdbc:mysql://192.168.1.1:3306/db?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
    spring.datasource.username=root
    spring.datasource.password=666
    

    ​ ​ 在单数据源的配置中,DataSource和SqlSessionFactory等都封装了默认的,但是如果使用多数据源的话,就需要单独配置了:

    ​ ​ 配置类:

    @Configuration
    @MapperScan(basePackages = "com.dao", sqlSessionFactoryRef = "mysqlSqlSessionFactory")
    public class MysqlDataSourceConfig {
    
    
        //配置数据源
        @Bean(name = "mysqlDataSource")
        @Primary
        @ConfigurationProperties(prefix = "spring.datasource.mysql")
        public DataSource getMysqlDateSource() {
            return DataSourceBuilder.create().build();
        }
    
    
        //配置factory
        @Bean(name = "mysqlSqlSessionFactory")
        @Primary
        public SqlSessionFactory test1SqlSessionFactory(@Qualifier("mysqlDataSource") DataSource datasource) throws Exception {
            SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
            bean.setDataSource(datasource);
            bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/**/*.xml"));
            bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource("classpath:/config/mybatis-config.xml"));
            return bean.getObject();
        }
    

    ​ ​ 配置文件:

    spring.datasource.mysql.jdbc-url=jdbc:mysql://192.168.1.1:3306/db?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
    spring.datasource.mysql.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.mysql.type=org.apache.commons.dbcp2.BasicDataSource
    spring.datasource.mysql.username=root
    spring.datasource.mysql.password=666
    

    ​ ​ 如果增加数据源的话,上面的配置再来一对就好了。

    ​ ​ 到这里就结束了,谢谢大家,如有问题,欢迎探讨。

    ​ ​ 注:这篇文章主要讲的是为什么,很多代码都是删减过的,所以不适合直接使用。

    ​ ​ 附录:

    ​ ​ 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>
      <environments default="development">
        <environment id="development">
          <transactionManager type="JDBC"/>
          <dataSource type="POOLED">
            <property name="driver" value="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
          </dataSource>
        </environment>
      </environments>
      <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
      </mappers>
    </configuration>
    
    展开全文
  • 利用跳板机实现访问服务器和数据库 什么是跳板机? 跳板机(Jump Server):也称堡垒机,是一类可作为跳板批量操作的远程设备的网络设备,是系统管理员和运维人员常用的操作平台之一。 那么具体是做什么的呢? 现在...

    利用跳板机实现访问服务器和数据库

    什么是跳板机?

    跳板机(Jump Server):也称堡垒机,是一类可作为跳板批量操作的远程设备的网络设备,是系统管理员和运维人员常用的操作平台之一。
    那么具体是做什么的呢?
    现在一些比较大的互联网企业,往往拥有大量的服务器,为了能够统一方便管理,运维人员通过跳板机去管理对应的服务器。我们在访问服务器的时候,先通过登陆跳板机,然后再进入相应服务器。从一定程度上提升了服务器的数据安全性,也提升了服务器的可维护性。
    在这里插入图片描述

    利用跳板机访问服务器

    在这里我用xshell做实例,其他的工具原理一样,根据要求做对应更改即可
    前提:我们个人账号需要在跳板机和服务器提前开通对应权限
    然后我们打开Xshell
    在这里插入图片描述
    主机端口我们选择跳板机对应IP地址,然后点击SSH–隧道,添加TCP/IP转移
    在这里插入图片描述
    我们在这里目标主机上,输入我们真正需要访问的目标服务器的ip地址,监听端口我们可以自己定义。
    如果有必要,我们还需要配置网络代理,这个我们根据开发实际情况自己去定义。
    一般情况我们用户身份验证都是通过ssh密钥来实现
    在这里插入图片描述
    在这些准备工作都完成后,我们点击登陆跳板机,如果登陆失败,我们可以再重新梳理看是不是有些地方配置不完整。
    登陆目标服务器
    在配置好跳板机后,目标服务器的配置就相对简单了,我们配置中只要将主机目标地址端口设置为我们刚才在跳板机隧道中定义好的路径和端口即可。然后根据开发需求配置相应网络代理和ssh密钥身份验证即可。

    利用跳板机访问数据库

    当然数据库的访问也是需要通过跳板机来实现
    首先配置跳板机目标路径已经对应配置
    在这里插入图片描述
    然后在SSH配置中配置跳板机的相应配置
    在这里插入图片描述
    在这些配置完成后,我们便可以通过跳板机来访问目标数据库了。

    展开全文
  • 基于数据库实现的分布式锁

    万次阅读 多人点赞 2019-06-20 00:43:21
    在单机时代,虽然不需要分布式锁,但也面临过类似的问题,只不过在单机的情况下,如果有多个线程要同时访问某个共享资源的时候,我们可以采用线程间加锁的机制,即当某个线程获取到这个资源后,就立即对这个资源进行...
  • 数据库访问控制

    千次阅读 2019-11-27 21:49:51
    主体:一能够访问对象的实体,如通常 为进程或用户。 客体:被访问的对象,如文件,数据库、 表、元组、属性等。 访问权限:是指主体对客体可进行的特定 访问操作。如读、写、执行等。 访问控...
  • wordpress为网站系统提供了相同的数据表结构,为快速实现多个WordPress网站之间共享用户数据提供了可能。wordpress如何实现如网易通行证等大站一样的共享用户登录呢?只需要将需要共享的数据库共用即可,我们这里只...
  • 数据库的安全性

    千次阅读 2022-03-19 20:46:52
    应用程序写代码来实现对数据的访问控制,不必言,而数据库本身的安全性措施,如下所示 1、用户标识和鉴别 最外层的安全保护措施,即身份认证。常用方式有 1)口令认证 2)强身份认证 口令方式可能容易被窃听,不够...
  • 基于V#的ASP.NET.MVC 4 web 网站程序开发 ...我们首先要有一认知,就是ASP.NET.MVC 4 web 网站程序开发的三层架构概念,分别为BLL,DAL,MOD 这三层的概念,这是竖着的三层。横三层为view,Controllers...
  • 初学者一定要做的便是用户的注册与登录,同时牵涉到数据库的相关操作,可以利用Navicat建立一简单的数据库,在里面更新、插入一些数据。之后写python文件对数据库进行操作,我们可以写一MysqlHelp.py文件帮助...
  • 用户在具有了登录名之后,只能连接到SQLServer数据库服务器上,并不具有访问任何用户数据库的权限,只有成为了数据库的合法用户后,才能访问数据库。本节介绍如何对数据库用户进行管理。 ...
  • Java web项目中,无论项目是大是小,或多或少都会涉及到用户访问权限的控制,权限管理总体的设计思路就是,不该看的不看,不该做的不做!据我目前的了解,我所知道的几种实现访问权限控制的方式有: JQuery的zTree...
  • 数据库索引的应用与底层实现

    千次阅读 多人点赞 2018-04-21 02:00:48
    关于数据库索引,它的重要性体现在了方方面面,因此也不约而同的成为了面试中的一大要点。在我近期的腾讯、阿里巴巴面试中数据库索引也确实占据了一些分量,下午去腾讯现场面,还没来得及去看自己不懂的问题,晚上...
  • Mysql数据库读写分离的实现

    千次阅读 2020-05-16 16:45:08
    mysql-proxy是实现"读写分离(Read/Write Splitting)"的一软件(MySQL官方提供 ,也叫中间件),基本的原理是让主数据库处理写操作(insert、update、delete),而从数据库处理查询操作(select)。而数据库的一致...
  • Winform开发框架中实现同时兼容多种数据库类型处理 在很应用系统里面,虽然一般采用一种数据库运行,但是由于各种情况的需要,可能业务系统会部署在不同类型的数据库上,如果开发的系统能够很方便支持多种数据库的...
  • MySQL数据库面试题(2020最新版)

    万次阅读 多人点赞 2020-03-10 17:20:40
    数据库三大范式是什么mysql有关权限的表都有哪几MySQL的binlog有有几种录入格式?分别有什么区别?数据类型mysql有哪些数据类型引擎MySQL存储引擎MyISAM与InnoDB区别MyISAM索引与InnoDB索引的区别?InnoDB引擎的4...
  • 数据库分库分表策略的具体实现方案

    万次阅读 多人点赞 2017-01-02 14:10:03
    1、 使用Spring AOP实现MySQL数据库读写分离案例分析 2、MySQL5.6 数据库主从(Master/Slave)同步安装与配置详解 3、MySQL主从复制的常见拓扑、原理分析以及如何提高主从复制的效率总结 4、使用mysqlreplicate...
  • 作为一程序员,不了解数据库怎么能行,那么数据库到底是啥呢,作为一Java工程师,平时和数据库打交道着实不少,所谓的CRUD其实就是对数据库进行增删改查的操作。 根据百度百科的介绍,数据库是“按照数据...
  • navicat 多用户远程连接 mysql 数据库

    千次阅读 2016-11-05 21:36:55
    本文主要是自己想让多人拥有远程操作服务端数据库的能力总结出来的一些经验,希望能对想跟我实现同样想的人有所帮助
  • Unity C# 连接SQL Server数据库实现获取和添加登录注册的用户列表。 参考:C#操作SQL Server数据库 Github:C#操作SQL Server练习 Github:MyGameServer(服务器、连接数据库) Github:PhotonChatRoom...
  • 数据库设计

    万次阅读 2021-09-14 09:58:39
    数据库设计是指对于一给定的应用环境,构造(设计)优化的数据库逻辑模式和物理结构, 并据此建立数据库及其应用系统,使之能够有效地存储和管理数据,满足各种用户的应用需求,包括信息管理要求和数据操作要求。...
  • 数据库读写分离,主从同步实现方法

    万次阅读 多人点赞 2017-12-06 21:37:06
    通过实际的例子编码实现数据库读写分离,实现数据库主从同步
  • 本篇内容综合广大网友提供内容,笔者经过整理,对数据库连接池原理和实现过程做很系统的并且通俗易懂的分析讲解,以及手写一连接池实现过程作为演示。 一、早期通过JDBC方式操作数据库 我们先来看早期使用JDBC...
  • 使用Android Studio+Spring Boot+MySQL数据库实现博客APP

    千次阅读 多人点赞 2020-06-30 02:00:42
    使用Android Studio+Spring Boot+MySQL数据库实现博客APP为什么要写这技术说明开发环境主要功能部分UI设计(少量来源于网络)数据库说明运行说明遇到的问题及解决思路GitHub源码分享 为什么要写这 因为课程需要,...
  • 介绍几国产数据库

    万次阅读 2021-03-01 13:49:46
    达梦数据库具有如下技术特色:支持多个平台之间的互联互访、高效的并发控制机制、有效的查询优化策略、灵活的系统配置、支持各种故障恢复并提供多种备份和还原方式。 具有高可靠性、支持多种多媒体数据类型、提供...
  • 在上文Android入门案例(一)——简单登录中,我们只是简单接触Android,所以实现的是单机登录,这章我们接着完善登录的小案例,选择连接mysql云端数据库 一、准备 1.加载外部jar包 在Android工程中要使用jdbc的话...
  • 数据库连接池原理详解与自定义连接池实现

    万次阅读 多人点赞 2017-05-17 18:18:54
    实现原理数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数制约。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么的连接数量。连接池的...
  • 数据库访问技术的总结 一

    千次阅读 2018-06-12 19:18:33
    本文打算对数据库访问技术,做一完整的梳理,已被后来需要时查阅。 一、嵌入式SQL的处理过程 将书写的SQL语句嵌入到主语言中,主语言一般如c、c++、java等。对于这种嵌入式语言(ESQL),RDBMS的处理方式是...
  • CO3:掌握数据库SQL语言和数据库管理技术,能够对实现结果给出合理性解释,培养数据库操作访问数据库系统管理能力。 CO4:掌握数据库应用编程技术及其开发工具的使用,培养数据库应用编程能力,同时培养解决复杂工程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 799,160
精华内容 319,664
关键字:

数据库实现多个用户同时访问