精华内容
下载资源
问答
  •  设备a动态选择k1,k3字段为某种数据 设备b选择 k1,k2 设备c选择k3,k2 如何将这些设备分别对应的字段和字段的意思进行区分࿰c;最好可以动态选择字段࿰c; 比如服务器添加一个设备d࿰c;字段为k1 在不...
  •  CArray类支持与C arrays相似的数组,但是必要时可以动态压缩并扩展。数组索引从0开始。可以决定是固定数组上界还是允许当添加元素时扩展当前的边界。内存对上界是连续地分配空间,甚至一些元素可为空。  和C...
      CArray
    
      需要包含的头文件 <afxtempl.h>
      CArray类支持与C arrays相似的数组,但是必要时可以动态压缩并扩展。数组索引从0开始。可以决定是固定数组上界还是允许当添加元素时扩展当前的边界。内存对上界是连续地分配空间,甚至一些元素可为空。
      和C arrays一样,CArray索引元素的访问时间是不变的,与数组大小无关。
      提示:
      在使用一个数组之前,使用SetSize建立它的大小和为它分配内存。如果不使用SetSize,则为数组添加元素就会引起频繁地重新分配和拷贝。频繁地重新分配和拷贝不但没有效率,而且导致内存碎片
      如果需要一堆数组中的个别数据,必须设置CDumpContext对象的深度为1或更大。
      此类的某成员函数调用全局帮助函数,它必须为CArray的大多数使用而定制。请参阅宏和全局量部分中的“类收集帮助器”。
      当从一个CArray对象中移去元素时,帮助函数DestructElements被调用。
      当添加元素时,帮助函数ConstructElements被调用。
      数组类的派生与列表的派生相似。
      MFC提供了一套模板库,来实现一些比较常见的数据结构如Array,List,Map。CArray即为其中的一个,用来实现动态数组的功能。CArray是从CObject派生,有两个模板参数,第一个参数就是CArray类数组元素的变量类型,后一个是函数调用时的参数类型。有一个类 class Object,要定义一个Object的动态数组,那么可以用以下两种方法:
      CArray<Object,Object> Var1;
      CArray<Object,Object&> Var2;
      Var2的效率要高。
      先了解一下CArray中的成员变量及作用。TYPE* m_pData; // 数据保存地址的指针
      int m_nSize; // 用户当前定义的数组的大小
      int m_nMaxSize; // 当前实际分配的数组的大小
      int m_nGrowBy; // 分配内存时增长的元素个数
      构造函数,对成员变量进行了初始化。
      CArray<TYPE, ARG_TYPE>::CArray()
      {
      m_pData = NULL;
      m_nSize = m_nMaxSize = m_nGrowBy = 0;
      }
      SetSize成员函数是用来为数组分配空间的。SetSize的函数定义如下:
      void SetSize( int nNewSize, int nGrowBy = -1 );
      nNewSize 指定数组的大小
      nGrowBy 如果需要增加数组大小时增加的元素的个数。
      对SetSize的代码,进行分析。
      void CArray<TYPE, ARG_TYPE>::SetSize(int nNewSize, int nGrowBy)
      {
      if (nNewSize == 0)
      {
      // 第一种情况
      // 当nNewSize为0时,需要将数组置为空,
      // 如果数组本身即为空,则不需做任何处理
      // 如果数组本身已含有数据,则需要清除数组元素
      if (m_pData != NULL)
      {
      //DestructElements 函数实现了对数组元素析构函数的调用
      //不能使用delete m_pData 因为我们必须要调用数组元素的析构函数
      DestructElements<TYPE>(m_pData, m_nSize);
      //现在才能释放内存
      delete[] (BYTE*)m_pData;
      m_pData = NULL;
      }
      m_nSize = m_nMaxSize = 0;
      }
      else if (m_pData == NULL)
      {
      // 第二种情况
      // 当m_pData==NULL时还没有为数组分配内存
      //首先我们要为数组分配内存,sizeof(TYPE)可以得到数组元素所需的字节数
      //使用new 数组分配了内存。注意,没有调用构造函数
      m_pData = (TYPE*) new BYTE[nNewSize * sizeof(TYPE)];
      //下面的函数调用数组元素的构造函数
      ConstructElements<TYPE>(m_pData, nNewSize);
      //记录下当前数组元素的个数
      m_nSize = m_nMaxSize = nNewSize;
      }
      else if (nNewSize <= m_nMaxSize)
      {
      // 第三种情况
      // 这种情况需要分配的元素个数比已经实际已经分配的元素个数要少
      if (nNewSize > m_nSize)
      {
      // 需要增加元素的情况
      // 与第二种情况的处理过程,既然元素空间已经分配,
      // 只要调用新增元素的构造函数就Ok
      ConstructElements<TYPE>(&m_pData[m_nSize], nNewSize-m_nSize);
      }
      else if (m_nSize > nNewSize)
      {
      // 现在是元素减少的情况,我们是否要重新分配内存呢?
      // No,这种做法不好,后面来讨论。
      // 下面代码释放多余的元素,不是释放内存,只是调用析构函数
      DestructElements<TYPE>(&m_pData[nNewSize], m_nSize-nNewSize);
      }
      m_nSize = nNewSize;
      }
      else
      {
      //这是最糟糕的情况,因为需要的元素大于m_nMaxSize,
      // 意味着需要重新分配内存才能解决问题
      // 计算需要分配的数组元素的个数
      int nNewMax;
      if (nNewSize < m_nMaxSize + nGrowBy)
      nNewMax = m_nMaxSize + nGrowBy;
      else
      nNewMax = nNewSize;
      // 重新分配一块内存
      TYPE* pNewData = (TYPE*) new BYTE[nNewMax * sizeof(TYPE)];
      //实现将已有的数据复制到新的的内存空间
      memcpy(pNewData, m_pData, m_nSize * sizeof(TYPE));
      // 对新增的元素调用构造函数
      ConstructElements<TYPE>(&pNewData[m_nSize], nNewSize-m_nSize);
      //释放内存
      delete[] (BYTE*)m_pData;
      //将数据保存
      m_pData = pNewData;
      m_nSize = nNewSize;
      m_nMaxSize = nNewMax;
      }
      }
      下面是ConstructElements函数的实现代码template<class TYPE>
      AFX_INLINE void AFXAPI ConstructElements(TYPE* pElements, int nCount)
      {
      // first do bit-wise zero initialization
      memset((void*)pElements, 0, nCount * sizeof(TYPE));
      for (; nCount--; pElements++)
      ::new((void*)pElements) TYPE;
      }
      ConstructElements是一个模板函数。对构造函数的调用是通过标为黑体的代码实现的。可能很多人不熟悉new 的这种用法,它可以实现指定的内存空间中构造类的实例,不会再分配新的内存空间。类的实例产生在已经分配的内存中,并且new操作会调用对象的构造函数。因为vc中没有办法直接调用构造函数,而通过这种方法,巧妙的实现对构造函数的调用。
      再来看DestructElements 函数的代码template<class TYPE>
      AFX_INLINE void AFXAPI DestructElements(TYPE* pElements, int nCount)
      {
      for (; nCount--; pElements++)
      pElements->~TYPE();
      }
      DestructElements函数同样是一个模板函数,实现很简单,直接调用类的析构函数即可。
      如果定义一个CArray对象 CArray<Object,Object&> myObject ,对myObject就可象数组一样,通过下标来访问指定的数组元素。
      CArray[]有两种实现,区别在于返回值不同。
      template<class TYPE, class ARG_TYPE>
      AFX_INLINE TYPE CArray<TYPE, ARG_TYPE>::operator[](int nIndex) const
      { return GetAt(nIndex); }
      template<class TYPE, class ARG_TYPE>
      AFX_INLINE TYPE& CArray<TYPE, ARG_TYPE>::operator[](int nIndex)
      { return ElementAt(nIndex); }
      前一种情况是返回的对象的实例,后一种情况是返回对象的引用。分别调用不同的成员函数来实现。
      TYPE GetAt(int nIndex) const
      { ASSERT(nIndex >= 0 && nIndex < m_nSize);
      return m_pData[nIndex]; }
      TYPE& ElementAt(int nIndex)
      { ASSERT(nIndex >= 0 && nIndex < m_nSize);
      return m_pData[nIndex]; }
      除了返回值不同,其它都一样.
      CArray<int,int&> arrInt;
      arrInt.SetSize(10);
      int n = arrInt.GetAt(0);
      int& l = arrInt.ElementAt(0);
      cout << arrInt[0] <<endl;
      n = 10;
      cout << arrInt[0] <<endl;
      l = 20;
      count << arrInt[0] << endl;
      结果会发现,n的变化不会影响到数组,而l的变化会改变数组元素的值。实际即是对C++中引用运算符的运用。
      CArray下标访问是非安全的,它并没有超标预警功能。虽然使用ASSERT提示,但下标超范围时没有进行处理,会引起非法内存访问的错误。
      Add函数的作用是向数组添加一个元素。下面是它的定义: int CArray<TYPE, ARG_TYPE>::Add(ARG_TYPE newElement).Add函数使用的参数是模板参数的二个参数,也就是说,这个参数的类型是我们来决定的,可以使用Object或Object&的方式。熟悉C++的朋友都知道,传引用的效率要高一些。如果是传值的话,会在堆栈中再产生一个新的对象,需要花费更多的时间。
      template<class TYPE, class ARG_TYPE>
      AFX_INLINE int CArray<TYPE, ARG_TYPE>::Add(ARG_TYPE newElement)
      {
      int nIndex = m_nSize;
      SetAtGrow(nIndex, newElement);
      return nIndex;
      }
      它实际是通过SetAtGrow函数来完成这个功能的,它的作用是设置指定元素的值。
      template<class TYPE, class ARG_TYPE>
      void CArray<TYPE, ARG_TYPE>::SetAtGrow(int nIndex, ARG_TYPE newElement)
      {
      if (nIndex >= m_nSize)
      SetSize(nIndex+1, -1);
      m_pData[nIndex] = newElement;
      }
      SetAtGrow的实现也很简单,如果指定的元素已经存在,就把改变指定元素的值。如果指定的元素不存在,也就是 nIndex>=m_nSize的情况,就调用SetSize来调整数组的大小
      首先定义
      CArray<char *> arryPChar;
      这里以定义char*的为例子。
      接下来我们来熟悉CArray这个类里的函数。
      INT_PTR GetCount() const;
      获得当前这个数组有多少个元素。
      void SetSize(INT_PTR nNewSize, INT_PTR nGrowBy = -1);
      设置数组的大小。
      TYPE& GetAt(INT_PTR nIndex);
      void SetAt(INT_PTR nIndex, ARG_TYPE newElement);
      获得/设置序列的元素
      INT_PTR Add(ARG_TYPE newElement);
      在数组的末尾添加一个元素,数组的长度加1。如果之前使用SetSize是nGrowBy大于1,则内存按照nGrowBy增加。函数返回newElement的数组元素索引
      void RemoveAt(INT_PTR nIndex, INT_PTR nCount = 1);
      从指定的nIndex位置开始,删除nCount个数组元素,所有元素自动下移,并且减少数组的上限,但是不释放内存。这里我们自己手动的申请的就必须自己释放。new对应delete相信大家都知道的。
      void RemoveAll();
      从数组中移除素有的元素,如果数组为空,该行数也起作用。
      INT_PTR Append(const CArray& src);
      将同个类型的一个数组A附加到本数组的尾部,返回A第一数组元素在本数组的索引。
      void InsertAt(INT_PTR nIndex, ARG_TYPE newElement, INT_PTR nCount = 1);
      void InsertAt(INT_PTR nStartIndex, CArray* pNewArray);
      在指定的nIndex或者nStartIndex位置插入nCount个newElement数组元素或者pNewArray数组
      下面是我应用的实例:
      view plaincopy to clipboardprint?
      CArray <char*>arrPChar;
      //初始化元素
      arrPChar.SetSize(10);
      for (int i=0;i<10;i++)
      {
      char *aChar=new char[10];
      strcpy_s(aChar,10,"hello arr");
      arrPChar.SetAt(i,aChar);
      }
      //在数组的末尾插入一个元素
      char *bChar = new char[10];
      strcpy_s(bChar,10,"asdfefdsd");
      arrPChar.Add(bChar);
      //在索引2的位置插入一个元素,即在第三位插入一个元素
      char *cChar=new char[5];
      strcpy_s(cChar,5,"aidy");
      arrPChar.InsertAt(2,cChar);
      for (int j=0;j<arrPChar.GetCount();j++)
      {
      TRACE("%d,%s\n",j,arrPChar.GetAt(j));
      }
      //删除数组里的所有元素,要释放内存,如果单单Remove的话则内存不会被释放
      //这里因为使用RemoveAll的话内存无法被释放,所以没有给实例。
      int count = arrPChar.GetCount();
      for (int k=0; k<count; k++)
      {
      char *dChar=arrPChar.GetAt(0);
      arrPChar.RemoveAt(0);
      delete dChar;
      }
    展开全文
  • 公司做一个实时监控有一个地方需要把后台推送的数据动态的使用表格进行展示 我知道有一些插件可以做,但问题是我找的那个插件发现动态更新数据时IE内存一直累积,最后会造成崩溃现象 使用别人的插件说起来是效果好...
  • 需要动态传入数据源信息(用户名、密码、url等信息)࿰c;分析了一下源码࿰c;使用atomikos创建的数据源 是使用 AtomikosXaDataSourceBean 创建的࿰c;但是底层不能像AbsoultRoutDataSource去...
  • C动态内存分配

    2019-04-29 10:43:28
    在编写程序时,通常并不知道需要处理的数据量,或者难以评估所需处理数据量的变动程度。在这种情况下,要达到有效的资源利用,必须在运行时动态地分配所需内存,并在使用完毕后尽早释放不需要的内存,这就是动态内存...

    在编写程序时,通常并不知道需要处理的数据量,或者难以评估所需处理数据量的变动程度。在这种情况下,要达到有效的资源利用,必须在运行时动态地分配所需内存,并在使用完毕后尽早释放不需要的内存,这就是动态内存管理原理。

    动态内存管理同时还具有一个优点:当程序在具有更多内存的系统上需要处理更多数据时,不需要重写程序


    参考文章:

    [1] C语言动态内存管理和动态内存分配


    1、动态内存管理

    标准库 stdlib.h 提供以下四个函数用于动态内存管理:

    (1) malloc()、calloc():分配新的内存区域

    (2) realloc():调整已分配的内存区域

    (3) free():释放已分配的内存区域

    对象在内存中所占空间的大小是以字节数量为单位计算的。许多头文件(包括 stdlib.h)专门定义了类型 size_t 用来保存这种内存空间的相关信息。例如,sizeof 运算符以类型 size_t 返回字节数量。

    2、动态内存分配

    两个内存分配函数 malloc()和 calloc()的参数不一样:

    ① malloc( )  

    void*malloc(size_t size);
    • 函数 malloc()分配连续的内存区域,其大小不小于 size。当程序通过 malloc()获得内存区域时,内存中的内容尚未决定。
    #include <stdlib.h>
    #include <stdio.h>
    
    int main()
    {
        int *ptr = malloc(3*sizeof(int));
        if(ptr == NULL)
            return -1;
        else{
            printf("*ptr = %d\n",*ptr);
            *ptr = 3;
            *(ptr+1) = 4;
            *(ptr+2) = 5;
            for(int i = 0;i < 3;i++)
                printf("ptr %d = %d\n",i,ptr[i]);
        }
        printf("Hello World!\n");
        free(ptr);
        return 0;
    }
    

    输出:

    ② calloc( )

    void*calloc(size_t count,size_t size);
    • 函数 calloc()分配一块内存区域,其大小至少是 count_size。换句话说,上述语句分配的空间应足够容纳一个具有 count 个元素的数组,每个元素占用 size 个字节。而且,calloc()会把内存中每个字节都初始化为 0。
    #include <stdlib.h>
    #include <stdio.h>
    
    int main()
    {
        //数组大小未知
        int n;
        scanf("%d",&n);
        int *ptr = calloc(n,sizeof(int));
        if(ptr == NULL)
            return -1;
        else{
            printf("*ptr = %d\n",*ptr);
            for(int i = 0;i < n;i++)
                *(ptr+i) = i;
            for(int i = 0;i < n;i++)
                printf("ptr %d = %d\n",i,ptr[i]);
        }
        printf("Hello World!\n");
        free(ptr);
        return 0;
    }
    

    输出:初始值为0

    两个函数都返回 void 指针,这种指针被称为无类型指针(typeless pointer)。返回指针的值是所分配内存区域中第一个字节的地址,当分配内存失败时,返回空指针。

    当程序将这个 void 指针赋值给不同类型的指针变量时,编译器会隐式地进行相应的类型转换。然而,一些程序员倾向于使用显式类型转换。当获取所分配的内存位置时,所使用的指针类型决定了该如何翻译该位置的数据。


     

     

    展开全文
  • 摘要:采用ARM11微处理器SC6410结合485及以太网通信构建了嵌入式Linux上的WEB服务器,实现了对太阳能发电站监测系统的动态数据采集、分析、处理、存储和显示。实际长期运行表明,该系统能够稳定完整地监测现场设备...
  • springboot使用mybatis多数据动态切换的实现

    万次阅读 多人点赞 2018-01-23 12:39:31
    需求:项目使用了读写分离,或者数据进行了分库处理,我们希望在操作不同的数据库的时候,我们的程序能够动态的切换到相应的数据库,执行相关的操作。 首先,你需要一个能够正常运行的springboot项目,配置mybatis...

    需求:项目使用了读写分离,或者数据进行了分库处理,我们希望在操作不同的数据库的时候,我们的程序能够动态的切换到相应的数据库,执行相关的操作。

    首先,你需要一个能够正常运行的springboot项目,配置mybatis并且能够正常的操作数据库(增删查改)

    现在开始实现:

    思路:现在项目的结构设计基本上是基于MVC的,那么数据库的操作集中在dao层完成,主要业务逻辑在service层处理,controller层处理请求。假设在执行dao层代码之前能够将数据源(DataSource)换成我们想要执行操作的数据源,那么这个问题就解决了。

    虽然思路是有了,但是怎么换呢?
    爬了很多的博客查看了下官方的文档,我找到了这样一个类:AbstractRoutingDataSource,它继承于AbstractDataSourceAbstractDataSource又实现了DataSource接口,是一个标准的数据源。

    查看AbstractRoutingDataSource类:

    /**
     * Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
     * calls to one of various target DataSources based on a lookup key. The latter is usually
     * (but not necessarily) determined through some thread-bound transaction context.
     *
     * @author Juergen Hoeller
     * @since 2.0.1
     * @see #setTargetDataSources
     * @see #setDefaultTargetDataSource
     * @see #determineCurrentLookupKey()
     */
     //翻译结果如下
    /**
     * 抽象 {@link javax.sql.DataSource} 路由 {@link #getConnection ()} 的实现
     * 根据查找键调用不同的目标数据之一。后者通常是
     * (但不一定) 通过某些线程绑定事务上下文来确定。
     *
     * @author 史塔克 Hoeller
     * @since 2.0。1
     * @see #setTargetDataSources
     * @see #setDefaultTargetDataSource
     * @see #determineCurrentLookupKey ()
     */
    public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
    
        .......
    
        /**
         * Specify the map of target DataSources, with the lookup key as key.
         * The mapped value can either be a corresponding {@link javax.sql.DataSource}
         * instance or a data source name String (to be resolved via a
         * {@link #setDataSourceLookup DataSourceLookup}).
         * <p>The key can be of arbitrary type; this class implements the
         * generic lookup process only. The concrete key representation will
         * be handled by {@link #resolveSpecifiedLookupKey(Object)} and
         * {@link #determineCurrentLookupKey()}.
         */
         //翻译如下
        /**
         *指定目标数据源的映射,查找键为键。
         *映射的值可以是相应的{@link javax.sql.DataSource}
         *实例或数据源名称字符串(要通过
         * {@link #setDataSourceLookup DataSourceLookup})。
         *键可以是任意类型的; 这个类实现了
         *通用查找过程只。 具体的关键表示将
         *由{@link #resolveSpecifiedLookupKey(Object)}和
         * {@link #determineCurrentLookupKey()}。
        */
        public void setTargetDataSources(Map<Object, Object> targetDataSources) {
            this.targetDataSources = targetDataSources;
        }
    
        ......
    
        /**
         * Determine the current lookup key. This will typically be
         * implemented to check a thread-bound transaction context.
         * <p>Allows for arbitrary keys. The returned key needs
         * to match the stored lookup key type, as resolved by the
         * {@link #resolveSpecifiedLookupKey} method.
         */
         //翻译如下
         /**
          * 确定当前的查找键。这通常会
          * 实现以检查线程绑定的事务上下文。
          * <p> 允许任意键。返回的密钥需要
          * 与存储的查找密钥类型匹配, 如
          * {@link #resolveSpecifiedLookupKey} 方法。
         */
        protected abstract Object determineCurrentLookupKey();
    
    }

    我这里只是选择性的贴出了部分源码,详细信息大家可以在IDE里直接点进去查看。翻译的很鸡肋,但是可以明白,这个类的基本运作方式,它是一个abstract类,所以我们使用的话,推荐的方式是创建一个类来继承它并且实现它的determineCurrentLookupKey()方法,这个方法介绍上面也进行了说明,就是通过这个方法进行数据源的切换,这个时候你会又疑问,我没设置数据源,它是怎么切换的?上面我贴出了另外一个核心的方法setTargetDataSources(Map<Object, Object> targetDataSources),它需要一个Map,在方法注释中我们可以得知,这个Map存储的就是我们配置的多个数据源的键值对。我们整理一下这个类切换数据源的运作方式,这个类在连接数据库之前会执行determineCurrentLookupKey()方法,这个方法返回的数据将作为key去targetDataSources中查找相应的值,如果查找到相对应的DataSource,那么就使用此DataSource获取数据库连接。

    基本的原理已经说完了,接下来去项目里面配置就OK。

    1、创建枚举类DataSourceKey列出你所有的数据源名称,当然了,类名你可以按照自己的取名习惯,下面所有的类也是如此。

    public enum DataSourceKey {
        DB_MASTER,
        DB_SLAVE1,
        DB_SLAVE2,
        DB_OTHER
    }

    2、创建DynamicDataSourceContextHolder类,这个类是为了解决多线程访问全局变量的问题。

    import org.apache.commons.lang3.RandomUtils;
    import org.apache.log4j.Logger;
    
    /**
     * @author RocLiu [apedad@qq.com]
     * @version 1.0
     */
    public class DynamicDataSourceContextHolder {
        private static final Logger LOG = Logger.getLogger(DynamicDataSourceContextHolder.class);
    
        private static final ThreadLocal<DataSourceKey> currentDatesource = new ThreadLocal<>();
    
        /**
         * 清除当前数据源
         */
        public static void clear() {
            currentDatesource.remove();
        }
    
        /**
         * 获取当前使用的数据源
         *
         * @return 当前使用数据源的ID
         */
        public static DataSourceKey get() {
            return currentDatesource.get();
        }
    
        /**
         * 设置当前使用的数据源
         *
         * @param value 需要设置的数据源ID
         */
        public static void set(DataSourceKey value) {
            currentDatesource.set(value);
        }
    
        /**
         * 设置从从库读取数据
         * 采用简单生成随机数的方式切换不同的从库
         */
        public static void setSlave() {
            if (RandomUtils.nextInt(0, 2) > 0) {
                DynamicDataSourceContextHolder.set(DataSourceKey.DB_SLAVE2);
            } else {
                DynamicDataSourceContextHolder.set(DataSourceKey.DB_SLAVE1);
            }
        }
    }

    3、创建类DynamicRoutingDataSource继承AbstractRoutingDataSource类并且实现determineCurrentLookupKey()方法,设置数据源。

    import org.apache.log4j.Logger;
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
    
    public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
        private static final Logger LOG = Logger.getLogger(DynamicRoutingDataSource.class);
        @Override
        protected Object determineCurrentLookupKey() {
            LOG.info("当前数据源:{}"+ DynamicDataSourceContextHolder.get());
            return DynamicDataSourceContextHolder.get();
        }
    }

    4、配置数据源,这一步比较重要,创建配置类DynamicDataSourceConfiguration

    import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
    import com.apedad.example.commons.DataSourceKey;
    import com.apedad.example.commons.DynamicRoutingDataSource;
    import org.apache.ibatis.session.SqlSessionFactory;
    import org.mybatis.spring.SqlSessionFactoryBean;
    import org.mybatis.spring.SqlSessionTemplate;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    import org.springframework.transaction.PlatformTransactionManager;
    
    import javax.sql.DataSource;
    import java.util.HashMap;
    import java.util.Map;
    
    @MapperScan(basePackages = "com.apedad.example.dao")
    @Configuration
    public class DynamicDataSourceConfiguration {
        @Bean
        @ConfigurationProperties(prefix = "multiple.datasource.master")//此处的"multiple.datasource.master"需要你在application.properties中配置,详细信息看下面贴出的application.properties文件。
        public DataSource dbMaster() {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "multiple.datasource.slave1")
        public DataSource dbSlave1() {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "multiple.datasource.slave2")
        public DataSource dbSlave2() {
            return DruidDataSourceBuilder.create().build();
        }
    
        @Bean
        @ConfigurationProperties(prefix = "multiple.datasource.other")
        public DataSource dbOther() {
            return DruidDataSourceBuilder.create().build();
        }
    
        /**
         * 核心动态数据源
         *
         * @return 数据源实例
         */
        @Bean
        public DataSource dynamicDataSource() {
            DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
            dataSource.setDefaultTargetDataSource(dbMaster());
            Map<Object, Object> dataSourceMap = new HashMap<>(4);
            dataSourceMap.put(DataSourceKey.DB_MASTER, dbMaster());
            dataSourceMap.put(DataSourceKey.DB_SLAVE1, dbSlave1());
            dataSourceMap.put(DataSourceKey.DB_SLAVE2, dbSlave2());
            dataSourceMap.put(DataSourceKey.DB_OTHER, dbOther());
            dataSource.setTargetDataSources(dataSourceMap);
            return dataSource;
        }
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setDataSource(dynamicDataSource());
            //此处设置为了解决找不到mapper文件的问题
            sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
            return sqlSessionFactoryBean.getObject();
        }
    
        @Bean
        public SqlSessionTemplate sqlSessionTemplate() throws Exception {
            return new SqlSessionTemplate(sqlSessionFactory());
        }
    
        /**
         * 事务管理
         *
         * @return 事务管理实例
         */
        @Bean
        public PlatformTransactionManager platformTransactionManager() {
            return new DataSourceTransactionManager(dynamicDataSource());
        }
    }

    5、为了不影响业务代码而实现数据源切换,我决定使用AOP切换数据源,为了准确的知道哪个地方需要切换哪个数据源,我这里使用自定义注解的方式,如果你又更好的方式也推荐你使用自己的方式。创建自定义注解类:TargetDataSource:

    import com.apedad.example.commons.DataSourceKey;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TargetDataSource {
        DataSourceKey dataSourceKey() default DataSourceKey.DB_MASTER;
    }

    6、编写数据源切换切面类:DynamicDataSourceAspect

    import com.apedad.example.annotation.TargetDataSource;
    import org.apache.log4j.Logger;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    
    @Aspect
    @Order(-1)
    @Component
    public class DynamicDataSourceAspect {
        private static final Logger LOG = Logger.getLogger(DynamicDataSourceAspect.class);
    
        @Pointcut("execution(* com.apedad.example.service.*.list*(..))")
        public void pointCut() {
        }
    
        /**
         * 执行方法前更换数据源
         *
         * @param joinPoint        切点
         * @param targetDataSource 动态数据源
         */
        @Before("@annotation(targetDataSource)")
        public void doBefore(JoinPoint joinPoint, TargetDataSource targetDataSource) {
            DataSourceKey dataSourceKey = targetDataSource.dataSourceKey();
            if (dataSourceKey == DataSourceKey.DB_OTHER) {
                LOG.info(String.format("设置数据源为  %s", DataSourceKey.DB_OTHER));
                DynamicDataSourceContextHolder.set(DataSourceKey.DB_OTHER);
            } else {
                LOG.info(String.format("使用默认数据源  %s", DataSourceKey.DB_MASTER));
                DynamicDataSourceContextHolder.set(DataSourceKey.DB_MASTER);
            }
        }
    
        /**
         * 执行方法后清除数据源设置
         *
         * @param joinPoint        切点
         * @param targetDataSource 动态数据源
         */
        @After("@annotation(targetDataSource)")
        public void doAfter(JoinPoint joinPoint, TargetDataSource targetDataSource) {
            LOG.info(String.format("当前数据源  %s  执行清理方法", targetDataSource.dataSourceKey()));
            DynamicDataSourceContextHolder.clear();
        }
    
        @Before(value = "pointCut()")
        public void doBeforeWithSlave(JoinPoint joinPoint) {
            MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
            //获取当前切点方法对象
            Method method = methodSignature.getMethod();
            if (method.getDeclaringClass().isInterface()) {//判断是否为借口方法
                try {
                    //获取实际类型的方法对象
                    method = joinPoint.getTarget().getClass()
                            .getDeclaredMethod(joinPoint.getSignature().getName(), method.getParameterTypes());
                } catch (NoSuchMethodException e) {
                    LOG.error("方法不存在!", e);
                }
            }
            if (null == method.getAnnotation(TargetDataSource.class)) {
                DynamicDataSourceContextHolder.setSlave();
            }
        }
    }

    7、在springboot程序运行入口中设置取消自动配置数据源

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class SpringBootDynamicDatasourceStartedApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringBootDynamicDatasourceStartedApplication.class, args);
        }
    }

    8、使用,在你需要切换数据源的service方法上加上注解就OK,注意:如果你使用了接口对service层进行分离,那么注解需要添加到你的实现类的相关方法上。示例如下

    @Service("userInfoService")
    public class UserInfoServiceImpl implements UserInfoService {
        private static final Logger LOG = Logger.getLogger(UserInfoServiceImpl.class);
        @Resource
        private UserInfoMapper userInfoMapper;
    
        @TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)
        @Override
        public List<UserInfo> listAll() {
            return userInfoMapper.listAll();
        }
        //使用此注解来切换到想切换的数据源
        @TargetDataSource(dataSourceKey = DataSourceKey.DB_OTHER)
        @Override
        public int insert(UserInfo userInfo) {
            return userInfoMapper.insert(userInfo);
        }
    }

    测试用例这里就不赘述了,详情可以参考下面的源码。
    项目源码地址:https://github.com/Apedad/spring-boot-dynamic-datasource-started

    最后,有个问题需知,此处事务只能回滚默认数据源操作,如果需要回滚其他数据源的操作请使用分布式事务进行处理。

    展开全文
  • 数据还是原来的数据,只是需要重新处理下,上文链接:让你的数据动起来! 提取绘图需要数据,选择top10的国家 data = gapminder select.country = c(as.character(head(data$country[data$year==2007] [order...

    作者:小伍哥

     来源:AI入门学习

    上次的动图画的差不多了,但是还差一个柱状图没画,重新补上。数据还是原来的数据,只是需要重新处理下,上文链接:让你的数据动起来!

    提取绘图需要的数据,选择top10的国家

    data = gapminder
    select.country = c(as.character(head(data$country[data$year==2007]
                                         [order(data$pop[data$year==2007],decreasing = TRUE)],10)))
    data = data[data$country %in%select.country,]
    data$country = factor(data$country, order = TRUE, levels =select.country)

    制作Gif

    ggplot(data, aes(country, lifeExp,fill= country))
      +geom_bar(stat='identity', position ='dodge',show.legend = FALSE)
      +theme(axis.text.x = element_text(angle =45, hjust =0.5, vjust =0.5))
      +coord_flip()
      +labs(title ='Year: {frame_time}', x ='country', y ='life expectancy')
      +transition_time(year)
      +ease_aes('linear')

    颜色有点丑,我们换个颜色看看

    ggplot(data, aes(country, lifeExp,fill= country)) 
      +geom_bar(stat= 'identity', position = 'dodge',show.legend = FALSE)
      +theme(axis.text.x = element_text(angle = 45, hjust = 0.5, vjust = 0.5)) +coord_flip()
      +scale_fill_manual(
      values = alpha(c("red", 'Aquamarine1',"MistyRose", "Magenta","Cyan", "DarkOrchid1","Gold1","Purple","Blue2","DarkCyan")))
      +labs(title = 'Year: {frame_time}', x = 'country', y = 'life expectancy') 
      +transition_time(year)
      +ease_aes('linear')

    当然,这个也可以转换成视频,配上音乐,就是前段时间流行的图表了,制作比较简单。

     

    展开全文
  • 摘要:采用ARM11微处理器SC6410结合485及以太网通信构建了嵌入式Linux上的WEB服务器,实现了对太阳能发电站监测系统的动态数据采集、分析、处理、存储和显示。实际长期运行表明,该系统能够稳定完整地监测现场设备...
  • GCC处理静态库与动态库一、用gcc处理静态库与动态库1、用gcc生成.a静态库和.so的动态库(1)编辑所需要的程序hello.h,hello.c,main.c(2)将hello.c文件编译成hello.o文件(3)由.o文件创建静态库(4)在程序中使用静态库(5)...
  • <div><p>我的表单使用了你的插件࿰c;赞一个࿰c;... edit.html 支持写for 循环来动态处理? how ? 谢谢! </code></pre>该提问来源于开源项目:fallsea/fsLayui</p></div>
  • 1.为什么需要使用动态内存分配 数组的元素存储于内存中...为了避免空间的浪费和超出范围的处理需要使用动态内存分配。 2.常见的动态内存错误 动态内存分配最常见的错误就是忘记检查所请求的内存是否成功分...
  • 突发奇想想弄一个动态切换数据源的东东。于是在网上搜了很多关于配置...我需要让B方法访问b数据源,C方法访问c数据源,同时还要支持事务。这样的需求用前面的注解切数据源是不能实现的。会发现怎么搞都只能访问b或者c
  • 静态数组的缺点是:对于事先无法准确估计数据量的情况,无法做到既满足处理需要,又不浪费内存空间。 所谓动态数组是指,在程序运行过程中,根据实际需要指定数组的大小。 在C语言中,可利用内存的申请和释放库函数...
  • 一些应用程序必须在运行时接受(或构建)并处理各种SQL语句 这种在程序执行临时生成的 SQL 语句叫动态 SQL 语句. 利用动态 SQL 来编写 Pro*C 程序的方法叫动态 SQL 技术.。也就是说SQL语句的组成直到运行时才知道。...
  • 复习C字符串处理

    2007-12-20 11:07:00
    VC数据转换~好东西~~CString ,BSTR ,LPCTSTR之间关系和区别CString是一个动态TCHAR数组,BSTR是一种专有格式的字符串(需要用系统提供的函数来操纵,LPCTSTR只是一个常量的TCHAR指针。CString 是一个完全独立的类,...
  • 该系统不仅具有强大的测量和通信功能,而且具有组网方便、动态拓扑、低成本、大容量、高可靠性的特点,能很好地满足高科技工业园电能质量实时监测的实际需要。 系统概述 系统结构如图1所示,由数据采集节点,ARM9S...
  • 要完全利用这些能力,需要理解C动态内存管理怎么回事。 C程序在运行时环境中执行,这通常是有操作系统提供的环境,支持堆和栈以及其他的程序行为。 注意 C99引入了变长数组,即数组的大小不是在编译时确定而是...
  • 在外生,管道在处理单元,方面构建Cells ,通过数据路径,连接Tendrils ,这种形式向无环图, Plasms 。 单元格通常用C ++编写,卷须可以是任何类型,等离子可以以各种巧妙的方式执行。 Python被用作图形DSL。 在...
  •  数组有其优点与缺点:优点在于是简单,缺点是(1)无法预知长度,数组无法处理程序所需要使用的元素数量超过声明的长度这种情况(2)内存浪费(3)数据溢出 2、C函数库提供两个函数用于执行动态内存分配和释放...
  • Java与C#都只能调用C格式导出动态库,因为C数据类型比较单一,容易映射. 两者都是在本地端提供一套与之映射的C#/java描述接口,通过底层处理这种映射关系达到调用的目的. 一. 结构体的传递 Cpp代码 收藏代码#d
  • 基于c#CP3平面网严密平差数据处理 using System; using System.Collections.Generic; using System.Collections;//使用动态数组需要添加的语句 using System.ComponentModel; using System.Data; using System....
  • 现有一个ATI公司提供的.c的程序可以读取ait mini45的数据,因为想要在python里做数据处理,把.c改成python也需要大量的时间精力,而且c执行的速度也要比python快不是一点半点,所以用到python调用c动态库。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,229
精华内容 491
关键字:

c需要动态处理数据