引入
CopyManager主要用于远程执行数据库的copy命令,主要分为In和Out两方面。
从常见的**public long copyIn(final String sql, Reader from)**方法开始:
此处传入了默认的参数:
实际执行
参数
三个参数的含义分别为:
- sql 参数: 该参数是一个 COPY FROM STDIN 语句,用于指定复制操作的目标表以及复制的格式。COPY FROM STDIN 语句通常包括表名、列名、CSV 格式的选项(如分隔符、NULL 值的表示等)。
- from 参数: 这个参数代表数据的来源,通常是一个 CSV 文件或类似的文本数据流。它用于读取数据并发送给数据库服务器进行复制操作。
- bufferSize 参数: 这个参数指定了缓冲区的大小,单位是字符数。它用于暂存从 Reader 中读取的数据,然后将这些数据一次性发送到数据库服务器。
流程
流程很清晰:使用该sql新建一个CopyIn,每次写入bufferSize个char(最后一次除外),直到写完,执行cp.endCopy(),结束copy(cp.cancelCopy()可以大致理解为close,作用大概就是清缓存,未放入流程)。
内部实现
CopyIn cp = copyIn(sql)
看一下这行
queryExecutor.startCopy(sql, connection.getAutoCommit()); // 注意,该处的connection.getAutoCommit()是获取是否原子操作,该处的auto不是自动
实际实现如下:
不先关注事务内部和外部实现那一部分,只关注具体实现:
由发送的消息格式可以看出,这部分将该sql以简单查询的格式发出了。
接下来看看返回了什么:
processCopyResults(null, true)
该部分源码比较长,简单概括一下:我们之前向数据库发送了一条消息,这部分就是接收它返回的消息,然后做处理。
查阅官方文档可知,CopyInResponse的消息以’G’标识,查找到对应处理位置:
显而易见,新建了一个CopyDualImpl对象,并对其初始化,设置了结束接收消息标志,最后将该CopyDualImpl对象返回。
实际上,发送过来的消息肯定不止一个字符’Q’,还有一部分指示发送行格式和列数量和格式的部分,其它的部分在初始化接收并处理,最终设置为CopyDualImpl对象的属性,最终实现部分如下:
cp.writeToCopy
追踪到底层:
'd’代表着:Identifies the message as data.COPY,即告诉后端,之后我要发送的消息理解为’COPY’的数据。之后告诉后端要发送的消息大小。
cp.endCopy
显而易见,向后端发送copyIn结束指令,底层如下:
‘c’:Identifies the message as a -complete indicator.COPY,结束该copyIn
之后会收到后端发来的’Z’开头的消息,表明已经准备好接收下一条命令,并解锁:
cp.cancelCopy
浅提一下,该部分向后端发送以’f’开始的消息,并等待接收’Z’开头的消息:
‘f’:Identifies the message as a -failure indicator.COPY
总结
本质上就是约定好了消息格式,在该基础上进行socket通信。