- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
有没有人遇到过 Java 库(或一些代码)来编写 Postgres 的 COPY command 使用的 binary
格式? ?
它看起来很简单,但如果有人已经找到正确的元组数据格式,我也可以从这里开始。
实际上,即使只是描述所有数据类型的格式也会有所帮助。
谢谢。
最佳答案
你可以试试 PgBulkInsert ,它实现了 PostgreSQL 的二进制复制协议(protocol):
它也可以从 Maven 中央存储库中获得。
免责声明:我是项目作者。
我不想简单地宣传我的项目,还要写协议(protocol)。
首先我写了一个类PgBinaryWriter
,它包装了一个 DataOutputStream
并具有写入二进制协议(protocol) header 的方法,一种开始新行的方法(二进制复制协议(protocol)要求您为要插入的每一行写入列数)和一个 write
方法,它需要一个 IValueHandler<TTargetType>
用于编写给定的 Java 类型。
PgBinaryWriter
实现一个 AutoClosable
, 因为有必要写一个 -1
在冲洗和关闭流之前添加到流中。
IValueHandler<TTargetType>
需要 DataOutputStream
和一个值。它负责使用 PostgreSQL 二进制协议(protocol)格式写入给定值。
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package de.bytefish.pgbulkinsert.de.bytefish.pgbulkinsert.pgsql;
import de.bytefish.pgbulkinsert.de.bytefish.pgbulkinsert.exceptions.BinaryWriteFailedException;
import de.bytefish.pgbulkinsert.de.bytefish.pgbulkinsert.pgsql.handlers.IValueHandler;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.OutputStream;
public class PgBinaryWriter implements AutoCloseable {
/** The ByteBuffer to write the output. */
private transient DataOutputStream buffer;
public PgBinaryWriter() {
}
public void open(final OutputStream out) {
buffer = new DataOutputStream(new BufferedOutputStream(out));
writeHeader();
}
private void writeHeader() {
try {
// 11 bytes required header
buffer.writeBytes("PGCOPY\n\377\r\n\0");
// 32 bit integer indicating no OID
buffer.writeInt(0);
// 32 bit header extension area length
buffer.writeInt(0);
} catch(Exception e) {
throw new BinaryWriteFailedException(e);
}
}
public void startRow(int numColumns) {
try {
buffer.writeShort(numColumns);
} catch(Exception e) {
throw new BinaryWriteFailedException(e);
}
}
public <TTargetType> void write(final IValueHandler<TTargetType> handler, final TTargetType value) {
handler.handle(buffer, value);
}
@Override
public void close() {
try {
buffer.writeShort(-1);
buffer.flush();
buffer.close();
} catch(Exception e) {
throw new BinaryWriteFailedException(e);
}
}
}
安IValueHandler
是一个简单的界面,其中有一个 handle
采取DataOutputStream
的方法和一个值。
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package de.bytefish.pgbulkinsert.de.bytefish.pgbulkinsert.pgsql.handlers;
import java.io.DataOutputStream;
import java.lang.reflect.Type;
public interface IValueHandler<TTargetType> extends ValueHandler {
void handle(DataOutputStream buffer, final TTargetType value);
Type getTargetType();
}
了解协议(protocol)很重要,您必须编写 -1
当值为空时。为此,我编写了一个抽象基类来处理这种情况。
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package de.bytefish.pgbulkinsert.de.bytefish.pgbulkinsert.pgsql.handlers;
import de.bytefish.pgbulkinsert.de.bytefish.pgbulkinsert.exceptions.BinaryWriteFailedException;
import java.io.DataOutputStream;
public abstract class BaseValueHandler<T> implements IValueHandler<T> {
@Override
public void handle(DataOutputStream buffer, final T value) {
try {
if (value == null) {
buffer.writeInt(-1);
return;
}
internalHandle(buffer, value);
} catch (Exception e) {
throw new BinaryWriteFailedException(e);
}
}
protected abstract void internalHandle(DataOutputStream buffer, final T value) throws Exception;
}
然后可以实现各种 Java 类型的处理程序。这是 long
的示例.你可以找到GitHub 存储库 ( handlers ) 中的其他实现。
// Copyright (c) Philipp Wagner. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
package de.bytefish.pgbulkinsert.de.bytefish.pgbulkinsert.pgsql.handlers;
import java.io.DataOutputStream;
import java.lang.reflect.Type;
public class LongValueHandler extends BaseValueHandler<Long> {
@Override
protected void internalHandle(DataOutputStream buffer, final Long value) throws Exception {
buffer.writeInt(8);
buffer.writeLong(value);
}
@Override
public Type getTargetType() {
return Long.class;
}
}
现在终于要连接零件了。请注意,我已经抽象了更多部分。可能需要在代码中查找更多实现细节。
public abstract class PgBulkInsert<TEntity> {
// ...
public void saveAll(PGConnection connection, Stream<TEntity> entities) throws SQLException {
CopyManager cpManager = connection.getCopyAPI();
CopyIn copyIn = cpManager.copyIn(getCopyCommand());
int columnCount = columns.size();
try (PgBinaryWriter bw = new PgBinaryWriter()) {
// Wrap the CopyOutputStream in our own Writer:
bw.open(new PGCopyOutputStream(copyIn));
// Insert all entities:
entities.forEach(entity -> {
// Start a New Row:
bw.startRow(columnCount);
// Insert the Column Data:
columns.forEach(column -> {
try {
column.getWrite().invoke(bw, entity);
} catch (Exception e) {
throw new SaveEntityFailedException(e);
}
});
});
}
}
private String getCopyCommand()
{
String commaSeparatedColumns = columns.stream()
.map(x -> x.columnName)
.collect(Collectors.joining(", "));
return String.format("COPY %1$s(%2$s) FROM STDIN BINARY",
table.GetFullQualifiedTableName(),
commaSeparatedColumns);
}
}
PgBulkInsert 支持以下 PostgreSQL 数据类型。
想象一下,应该将大量人员批量插入到 PostgreSQL 数据库中。每个Person
有名字、姓氏和出生日期。
PostgreSQL 数据库中的表可能如下所示:
CREATE TABLE sample.unit_test
(
first_name text,
last_name text,
birth_date date
);
应用程序中的域模型可能如下所示:
private class Person {
private String firstName;
private String lastName;
private LocalDate birthDate;
public Person() {}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
}
然后你必须实现 PgBulkInsert<Person>
,它定义了表和域模型之间的映射。
public class PersonBulkInserter extends PgBulkInsert<Person>
{
public PersonBulkInserter() {
super("sample", "unit_test");
MapString("first_name", Person::getFirstName);
MapString("last_name", Person::getLastName);
MapDate("birth_date", Person::getBirthDate);
}
}
最后我们可以编写单元测试来插入 100000
人员入库。您可以在 GitHub 上找到整个单元测试:IntegrationTest.java .
@Test
public void bulkInsertPersonDataTest() throws SQLException {
// Create a large list of Persons:
List<Person> persons = getPersonList(100000);
// Create the BulkInserter:
PersonBulkInserter personBulkInserter = new PersonBulkInserter();
// Now save all entities of a given stream:
personBulkInserter.saveAll(PostgreSqlUtils.getPGConnection(connection), persons.stream());
// And assert all have been written to the database:
Assert.assertEquals(100000, getRowCount());
}
private List<Person> getPersonList(int numPersons) {
List<Person> persons = new ArrayList<>();
for (int pos = 0; pos < numPersons; pos++) {
Person p = new Person();
p.setFirstName("Philipp");
p.setLastName("Wagner");
p.setBirthDate(LocalDate.of(1986, 5, 12));
persons.add(p);
}
return persons;
}
关于为 Postgres COPY 编写二进制格式的 Java 库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14242117/
有没有办法使用 Clojure format(基于 java.util.Formatter)或 cl-format(基于 Common Lisp 的format) 以编程方式设置空格填充?如果您事先知
我正在尝试创建一个用户实体以及数据/文件(pdf格式)。上传并保存到数据库很好,但是当我让用户进入 postman 时尝试发送获取请求方法,然后在数据字段中显示一些糟糕的数据,而且我无法在数据库中看到
我必须将值为 {"STX","ETX"} 的普通字符串数组转换为十六进制值,并且我应该根据 http://www.asciitable.com/ 得到 {2,3} . 最佳答案 听起来你想要一个 Ma
我想格式化我的代码,但不确定哪种格式类型最适合我的项目需要。 我发现仅对于 dart 和 flutter 项目(我都有),有不止一个选项可用于格式化编程语言/框架中预先构建的代码。 Dart : da
我已经尝试了多个代码,例如这样 Sub DateFixer() Application.ScreenUpdating = False Application.Calculation =
SolrQuery query = new SolrQuery(); query.setQuery("*:*"); query.add("wt","csv"); server.query(query)
我有一个包含多个字符串的数据库,我从查询中获取了这些记录,并且我在 QString 中收到了这种格式的数据: "Mon, 13 Nov 2017 09:48:45 +0000" 所以,我需要根据文化来
我有一个 Delphi 2007 DBGrid,我想让用户以更新的 Excel 格式 (OOXML) 保存它,但我的标准是用户不需要安装 Excel。有没有人知道任何已经这样做的组件?是的,我已经搜索
我正在我们的普通 html 站点旁边创建一个移动站点。使用 rails 3.1。移动站点在子域 m.site.com 中访问。 我已经定义了移动格式(Mime::Type.register_alias
我正在尝试使用 xmlstarlet 格式化 xml 文件,但我不想创建新的 xml 文件。 我试过了 xmlstarlet fo --inplace --indent-tab --omit-decl
我在 A 列中有一个带有文本的电子表格。 例如 A1=MY TEXT1 A2=MY TEXT2 A3=MY TEXT3 A4=MY TEXT4 A5=MY TEXT5 我想在文本的前后添加撇号 结果是
我想做一些源代码转换(自动导入列表清理),我想保留注释和格式。我听说过一些关于解析器这样做的事情,我认为是 ghc 解析器。 看起来我可以通过从文件中提取内容来使用 hs-src-exts Langu
我在 Excel 中工作,我想根据另一张表中的列表找出一张表中是否有匹配项。 我已将值粘贴到列表中,并希望从另一张表中返回它们的相应值。包含字母和数字的单元格可以正常工作(例如:D5765000),但
我有一个 DurationField在我的模型中定义为 day0 = models.DurationField('Duration for Monday', default=datetime.time
我正在为我的应用程序开发 WMI 查询。它需要为给定的 VID/PID 找到分配的虚拟 COM 端口。使用 WMI Code Creator 我发现...... 命名空间:root\CIMV2 类:W
我试图弄清楚如何使用 NSTextList,但除了 this SO question 之外,在网上几乎没有找到有用的信息。和 the comment in this blog . 使用这个我已经能够创
我要查询all_objects表在哪里last_ddl_time='01 jan 2010'但它拒绝日期格式... 任何机构给我查询的确切格式? 最佳答案 正如 AKF 所说,您应该使用 Trunc除
我试图在我的应用程序中实现聊天功能。我使用了 2 个 JEditorPane。一个用于保存聊天记录,另一个用于将聊天发送到前一个 JEditorPane。 JEditorPane 是 text/h
我在大学里修了一个编译器类(class),内容非常丰富,很有趣,尽管也很多工作。既然给了我们要实现的语言规范,所以我学不到的一件事就是语言设计。我现在正在考虑创建一种有趣的简单玩具语言,以便我可以玩耍
Closed. This question does not meet Stack Overflow guidelines。它当前不接受答案。 想改善这个问题吗?更新问题,以便将其作为on-topic
我是一名优秀的程序员,十分优秀!