Java 如何中止JDBC Postgresql CopyManager复制?

Java 如何中止JDBC Postgresql CopyManager复制?,java,multithreading,postgresql,jdbc,Java,Multithreading,Postgresql,Jdbc,有没有办法通过在单独的线程中调用copyIn()方法来取消复制过程 比方说,我有一个csv文件列表,我需要从中复制,以获得最大的数据库服务器功率。因此,我为n个文件创建了n个线程连接,但如果(例如)选择了错误的文件,我无法找到中止单个操作的方法 终止线程不起作用-复制只会继续运行 FutureTask类用于创建线程,因此有一个线程列表-每个csv一个 调用task.cancel(true)对服务器上的复制过程没有任何影响。只有System.exit() 有什么想法吗 我的一些代码: Upload

有没有办法通过在单独的线程中调用
copyIn()
方法来取消复制过程

比方说,我有一个csv文件列表,我需要从中复制,以获得最大的数据库服务器功率。因此,我为n个文件创建了n个线程连接,但如果(例如)选择了错误的文件,我无法找到中止单个操作的方法

终止线程不起作用-复制只会继续运行

FutureTask
类用于创建线程,因此有一个线程列表-每个csv一个

调用
task.cancel(true)
对服务器上的复制过程没有任何影响。只有
System.exit()

有什么想法吗

我的一些代码:

Uploader.java实现可调用

公共静态长上传文件(最终文件文件,最终字符串表名){
长状态=0;
试一试{
CopyManager CopyManager=
新建CopyManager((BaseConnection)新建数据源().connect());
FileReader=新的FileReader(文件);
status=copyManager.copyIn(sql,reader);
}catch(SQLException | IOException e){
...
}
返回状态;
}
@凌驾
public Long call()引发异常{
返回上传文件(文件、表名);
}
上传文件方法体

for(文件:文件){
未来任务ftask=
新的未来任务(
新上载程序(默认表名,文件)
);
任务。添加(ftask);
execService.execute(ftask);
}
已解决:

找到了解决方案,但需要对代码进行一些更改

上传文件方法体
现在看起来像这样

for(文件:文件){
Uploader Uploader=新的上传程序(默认表名,文件);
上传器。添加(上传器);
Future f=execService.submit(上传程序);
//保存未来以在完成时获取复制结果
}
有了这个,我们可以很容易地调用一些
上传程序
的方法,只要关闭数据库连接并正确处理异常即可。它将停止在服务器上复制


我承认这个解决方案可能不是最优雅的,但是它工作起来很快,而且不需要太多的代码。

免责声明:我没有尝试过,我只是通过查看源代码得到了这个想法

有一个
CopyManager.copyIn(String sql)
方法返回
copyIn
接口的实例,该接口又是
CopyOperation
的后代。该接口有一个
cancelCopy()
方法

请参见此处的JavaDocs:

但是采用流复制数据的方法只返回一个长值,因此无法使用其中使用的CopyOperation实例

但是,在查看copyIn()方法的源代码时,这似乎很容易自己完成

因此,与调用
copyIn(String,Reader)
不同,您基本上在代码中使用该方法中的代码:

// your code 
CopyManager copyManager = 
       new CopyManager((BaseConnection) new DataSource().connect());
FileReader from = ...  // different name!
int bufferSize = 65536;

// here starts the copy of the driver's implementation of the copyIn() method.

char[] cbuf = new char[bufferSize];
int len;

// if you store the instance of the CopyIn interface in an instance variable you 
// should be able to call cancelCopy() on it
CopyIn cp = copyManager.copyIn(sql);  

try {
    while ( (len = from.read(cbuf)) > 0) {
        byte[] buf = encoding.encode(new String(cbuf, 0, len));
        cp.writeToCopy(buf, 0, buf.length);
    }
    return cp.endCopy();
} finally { // see to it that we do not leave the connection locked
    if(cp.isActive())
        cp.cancelCopy();
}

PostgreSQL实际上不支持带内查询取消

当您从JDBC驱动程序请求查询取消时,它会建立一个新连接来发送取消消息。(这意味着,如果您处于
max_connections
cancel将失败,这有点反常)

结果是,你可以自己做同样的事情:

  • 在开始复制操作之前,使用
    pg\u backend\u pid()
    获取工作进程的进程ID

  • 如果要取消复制,请打开一个新连接,并发出
    pg\u cancel\u backend(?)
    ,并在前面记录pid。如果它没有停止,您可以稍等片刻,然后执行
    pg\u terminate\u backend(?)

这些是普通的SQL级别函数

唯一真正的问题是取消和终止请求是会话级别而不是语句级别。因此,他们可以与语句完成和新语句的开始竞争,例如:

  • 客户1:开始复制
  • 客户端2:连接以发送取消消息
  • 客户1:复印完成
  • 客户端1:开始新的单独副本
  • client2发送pg\u cancel\u后端(…)
此时,第二个副本将被终止,这可能不是您想要的。因此,您必须确保使用适当的排除客户端来防止这种情况发生,确保在启动新语句之前完成所有未完成的取消请求


IIRC JDBC驱动程序在内部也有同样的问题。这是团队真正想要一种方法来取消特定的每会话唯一语句序列号的原因之一,例如(当前不存在)pg_cancel_后端(pid,statementnumber)
,如果语句已经终止,它会因错误而中止,而不是发送cancel。

我刚刚尝试过。不幸的是,它不起作用,原因如下:我只能通过
FutureTask
get()
方法(记住多线程)返回
CopyIn
实例,该方法仅在线程完成任务时返回其结果。显然,当复制操作完成时,它会给我一个
CopyIn
的实例。