Pgjdbc执行COPY的记录
引
最近接触了Pgjdbc执行COPY命令的代码,在使用中也产生了一些想法,在此记录
简单的测试代码
从表中读入,写入另一张表
package org.postgresql.newTest;
import org.junit.Test;
import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.*;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
/**
* @author ljx
* @version 1.0
* @description: TODO
* @date 2024/8/23 11:19
*/
public class CopyTest {
private static final String DB_URL = "jdbc:postgresql://xxxxxxxxxx"
+ "/mydb";
private static final String USER = "testuser";
private static final String PASSWORD = "Helloworld";
//在windows上除非你给予用户对该文件夹完全控制的权限,否则无法自动新建文件
private static final String FILE_PATH = "D:\\javap\\testfile\\test2";
public void copyIn(String tabName) {
try (Connection connection = getConnection();
Reader reader = new FileReader(new File(FILE_PATH));) {
CopyManager copyManager = new CopyManager((BaseConnection) connection);
copyManager.copyIn("COPY " + tabName + " FROM STDIN ", reader);
} catch (Exception e) {
handleException(e);
}
}
private Connection getConnection() throws SQLException, ClassNotFoundException {
Class.forName("org.postgresql.Driver");
return DriverManager.getConnection(DB_URL, USER, PASSWORD);
}
private void handleException(Exception e) {
e.printStackTrace();
System.err.println("Error occurred while copying data: " + e.getMessage());
}
/**
* @description: 使用copyOut
* @param: tableName
* @return: void
* @author
* @date: 2024/8/23 15:55
*/
public void copyOut(String tableName) {
try {
Connection connection = getConnection();
//仅限于测试,使用时建议使用OutputStream
FileWriter fileWriter = new FileWriter(new File(FILE_PATH));
CopyManager copyManager = new CopyManager((BaseConnection) connection);
String copyStatement = "COPY " + tableName + " TO STDOUT ";
//上限1600,记得清理缓存
copyManager.copyOut(copyStatement, fileWriter);
fileWriter.close();
} catch (IOException | SQLException | ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Test
public void copyIOTest() {
copyOut("tab_test");
copyIn("tab_test_copy1");
}
}
读二进制数据,以bytea为例
首先忽略Pg有没有自动转换问题,仅仅进行测试二进制数据的读写
首先找一个图片文件,之后在表中以bytea格式存储该文件,之后使用getBytes方法读出其数据,并转为16进制,简单的输出一下。与原文件的16进制形式对比,我们发现其以\x开头,而其他部分没有变化,这显然是数据库对其进行的处理,再次推测,是否必须加上\x才是Pg内部的存储格式?而加上该\x之后,对图片的显示并无影响,因而推测,是否是所有的文件加上\x的二进制表示都不会影响使用?
测试将图片文件加上16进制的\x存入库内,再读出,查看图片是否正常,是否添加了两重的\x
代码如下:
public void copyByteaTest() throws IOException {
byte[] data = Files.readAllBytes(Paths.get("D:\\javap\\testfile\\test_addx.jpg"));
String chars = bytesToHex(data, 0);
//生成点测试数据
StringBuilder stringBuilder = new StringBuilder();
for (int i = 1; i <= 100; i++) {
stringBuilder.append(i);
stringBuilder.append(",");
stringBuilder.append(chars);
stringBuilder.append(",");
stringBuilder.append(chars);
if(i != 100){
stringBuilder.append("\n");
}
}
//写入测试文件
String tem =stringBuilder.toString();
byte[] datas = tem.getBytes();
OutputStream out = new FileOutputStream("D:\\javap\\testfile\\test_addx");
out.write(datas);
out.close();
//写入数据库
try (Connection connection = getConnection();
Reader reader = new FileReader(new File("D:\\javap\\testfile\\test_addx"));) {
CopyManager copyManager = new CopyManager((BaseConnection) connection);
copyManager.copyIn("COPY tab_test_copy1(id,img,img_copy) FROM STDIN WITH(DELIMITER ',') ", reader);
} catch (Exception e) {
handleException(e);
}
//从库中读出一个
try (Connection conn = getConnection();
Statement st = conn.createStatement();) {
ResultSet rs = st.executeQuery("SELECT * FROM tab_test_copy1 WHERE id = 1");
rs.next();
byte[] imgBytes = rs.getBytes(1);
// System.out.println(bytesToHex(imgBytes,0).substring(0,4));//检查是否双重\\x
FileOutputStream out2 = new FileOutputStream(new File("D:\\javap\\testfile\\test_addx_copy.jpg"));//图片格式是否毁坏
out2.write(imgBytes);
} catch (SQLException | ClassNotFoundException | IOException e) {
throw new RuntimeException(e);
}
}
经检查,只有一重\x,因而推测以\x开头应该是Pg数据库内部的一种保存机制,考虑到\x中若将\考虑为转义,其应该为\x,也就是16进制。