使用 jOOQ 转换 PostgreSQL UUID 数组的类型
Convert types of PostgreSQL UUID arrays using jOOQ
我正在尝试将 PostgreSQL (9.4) UUID
数组绑定到每个 UUID
周围的包装器 class 的数组。请注意,这些包装器集成在整个应用程序中,因此无法删除它们。我正在使用 jOOQ 和它的 Maven 插件 (3.5.1) 来生成 PoJo 的 Record
和 Table
classes.
我在绑定时遇到问题的数据库模式的相关部分类似于:
create table foo (
id uuid primary key,
ids uuid[] not null
);
然后我尝试使用 forcedType
元素转换 class,但是它生成的 classes 无法编译。放弃了,我只是打算让它们作为 UUID 生成,直到我遇到一些问题,其中数组值未转换为 uuid,并且 PostgreSQL 认为该数组是我查询中的文本数组 [1]。
为了解决这个问题,我尝试添加一个 Binding
[2] 和一个 Converter
[3],其中转换器用我们的包装器包装 UUID
和绑定将强制转换表达式添加到生成的 SQL。如果我编写流畅的查询 [4],这会很好地工作,但是在插入 Record
[5] 时无法正常工作。当我逐个构建数组查询时,插入语句以 'array.length - 1' 参数化部分结束。我怀疑我需要重写 Binding
class 的 #get
和 #set
方法,但是我发现文档在这方面有点简单。
所以我的问题是,在使用或不使用 Binding
class 的情况下,在 jOOQ 中绑定 UUID
数组的正确方法是什么?另外,在这个过程中是否可以将其转换为T数组?
- 查询(名称已更改)
public BazId getBazIdBySearchingFooIdsInReverse(
@NotNull final OtherId otherId,
@NotNull final SomethingId somethingId,
@NotNull final String barTypeName,
@NotNull final SomethingElseId somethingElseId) {
final Bar bar = BAR.as("bar");
final Foo foo = FOO.as("foo");
return db.select(BAZ.ID)
.from(BAZ)
.join(bar)
.on(BAZ.SOMETHING_ID.eq(bar.SOMETHING_ID))
.join(foo)
.on(bar.FOO_ID.eq(foo.ID))
.join("lateral unnest(foo.ids) with ordinality as x (id,ord)")
.on("x.id=foo.id")
.join(BAR_TYPE)
.on(bar.BAR_TYPE_ID.eq(BAR_TYPE.ID)
.and(BAR_TYPE.NAME.equalIgnoreCase(barTypeName)))
.where(BAZ.SOMETHING_ID.eq(somethingId))
.and(BAZ.SOMETHING_ELSE_ID.eq(somethingElseId))
.and(bar.OTHER_ID.eq(otherId))
.orderBy(DSL.field("x.ord").desc())
.limit(1)
.fetchOneInto(BazId.class); //Wraps a UUID
}
- 绑定
public class FooIdsBinding extends DefaultBinding<Object[], FooId[]> {
private static final long serialVersionUID = 0L;
private static final UUIDConverter converter = new UUIDConverter();
public FooIdsBinding() {
super(new FooIdsConverter());
}
@Override
public void sql(final BindingSQLContext<FooId[]> ctx) {
final RenderContext render = ctx.render();
render.sql("array[");
final UUID[] uuids = ctx.convert(converter).value();
for (int i = 0, last = uuids.length - 1; i <= last; ++i) {
render.visit(DSL.val(uuids[i])).sql("::uuid");
if (i != last) {
render.sql(',');
}
}
render.sql("]::uuid[]");
}
@Override
public void register(final BindingRegisterContext<FooId[]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY);
}
static class BaseUUIDConverter {
public FooId[] from(final Object[] from) {
return from == null ? null : Arrays.stream(from)
.map(that -> new FooId((UUID)that))
.collect(Collectors.toList())
.toArray(new FooId[from.length]);
}
public UUID[] to(final FooId[] from) {
return from == null ? null : Arrays.stream(from)
.map(FooId::getUuid)
.collect(Collectors.toList())
.toArray(new UUID[from.length]);
}
public Class<FooId[]> toType() {
return FooId[].class;
}
}
private static class UUIDConverter extends BaseUUIDConverter implements Converter<UUID[], FooId[]> {
@Override
public FooId[] from(final UUID[] that) {
return super.from(that);
}
@Override
public Class<UUID[]> fromType() {
return UUID[].class;
}
}
}
- 转换器。必须是
Object[]
才能使生成的 Table
编译
public class FooIdConverter extends FooIdsBinding.BaseUUIDConverter implements Converter<Object[],FooId[]> {
private static final long serialVersionUID = 0L;
@Override
public Class<Object[]> fromType() {
return (Class)UUID[].class;
}
}
- 有效的查询
db.insertInto(FOO)
.set(FOO.ID, new FooId())
.set(FOO.IDS, new FooId[]{new FooId(),new FooId()})
.execute();
- 不存在的查询
foo = new FooRecord();
foo.setId(new FooId());
foo.setIds(new FooId[]{new FooId(),new FooId()});
db.executeInsert(foo);
更新
我最终得到了这个绑定和转换器,它工作正常。我原以为我需要将数组的每个元素转换为一个 uuid,我的实现导致 jOOQ 的 sql 生成出现问题,但我认为我只在 jOOQ 覆盖之前看到与此相关的错误 Binding#register
为数组。
转换器
public class FooIdConverter implements Converter<Object[],FooId[]> {
private static final long serialVersionUID = 1L;
@Override
public FooId[] from(final Object[] from) {
return from == null ? null : Arrays.stream(from)
.map(that -> new FooId((UUID)that))
.collect(Collectors.toList())
.toArray(new FooId[from.length]);
}
@Override
public UUID[] to(final FooId[] from) {
return from == null ? null : Arrays.stream(from)
.map(FooId::getUuid)
.collect(Collectors.toList())
.toArray(new UUID[from.length]);
}
@Override
@SuppressWarnings("unchecked")
public Class<Object[]> fromType() {
return (Class)UUID[].class;
}
@Override
public Class<FooId[]> toType() {
return FooId[].class;
}
}
绑定
public class FooIdBinding extends DefaultBinding<Object[], FooId[]> {
private static final long serialVersionUID = 1L;
public FooIdBinding() {
super(new FooIdConverter());
}
@Override
public void sql(final BindingSQLContext<FooId[]> ctx) {
super.sql(ctx);
ctx.render().sql("::uuid[]");
}
@Override
public void register(final BindingRegisterContext<FooId[]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY);
}
@Override
public void get(final BindingGetResultSetContext<FooId[]> ctx) throws SQLException {
ctx.value(_convert(ctx.resultSet().getArray(ctx.index())));
}
@Override
public void get(final BindingGetStatementContext<FooId[]> ctx) throws SQLException {
ctx.value(_convert(ctx.statement().getArray(ctx.index())));
}
@Override
public void get(final BindingGetSQLInputContext<FooId[]> ctx) throws SQLException {
ctx.value(_convert(ctx.input().readArray()));
}
@Override
public void set(final BindingSetStatementContext<FooId[]> ctx) throws SQLException {
final PreparedStatement ps = ctx.statement();
ps.setArray(ctx.index(), ps.getConnection().createArrayOf("uuid", ctx.value()));
}
@Override
public void set(final BindingSetSQLOutputContext<FooId[]> ctx) throws SQLException {
throw new UnsupportedOperationException();
}
protected FooId[] _convert(final Array array) throws SQLException {
if (array == null) {
return null;
} else {
return converter().from(((UUID[]) array.getArray()));
}
}
}
jOOQ 代码生成器中似乎存在阻止 UUID[]
类型被覆盖的错误:#4388
我正在尝试将 PostgreSQL (9.4) UUID
数组绑定到每个 UUID
周围的包装器 class 的数组。请注意,这些包装器集成在整个应用程序中,因此无法删除它们。我正在使用 jOOQ 和它的 Maven 插件 (3.5.1) 来生成 PoJo 的 Record
和 Table
classes.
我在绑定时遇到问题的数据库模式的相关部分类似于:
create table foo (
id uuid primary key,
ids uuid[] not null
);
然后我尝试使用 forcedType
元素转换 class,但是它生成的 classes 无法编译。放弃了,我只是打算让它们作为 UUID 生成,直到我遇到一些问题,其中数组值未转换为 uuid,并且 PostgreSQL 认为该数组是我查询中的文本数组 [1]。
为了解决这个问题,我尝试添加一个 Binding
[2] 和一个 Converter
[3],其中转换器用我们的包装器包装 UUID
和绑定将强制转换表达式添加到生成的 SQL。如果我编写流畅的查询 [4],这会很好地工作,但是在插入 Record
[5] 时无法正常工作。当我逐个构建数组查询时,插入语句以 'array.length - 1' 参数化部分结束。我怀疑我需要重写 Binding
class 的 #get
和 #set
方法,但是我发现文档在这方面有点简单。
所以我的问题是,在使用或不使用 Binding
class 的情况下,在 jOOQ 中绑定 UUID
数组的正确方法是什么?另外,在这个过程中是否可以将其转换为T数组?
- 查询(名称已更改)
public BazId getBazIdBySearchingFooIdsInReverse(
@NotNull final OtherId otherId,
@NotNull final SomethingId somethingId,
@NotNull final String barTypeName,
@NotNull final SomethingElseId somethingElseId) {
final Bar bar = BAR.as("bar");
final Foo foo = FOO.as("foo");
return db.select(BAZ.ID)
.from(BAZ)
.join(bar)
.on(BAZ.SOMETHING_ID.eq(bar.SOMETHING_ID))
.join(foo)
.on(bar.FOO_ID.eq(foo.ID))
.join("lateral unnest(foo.ids) with ordinality as x (id,ord)")
.on("x.id=foo.id")
.join(BAR_TYPE)
.on(bar.BAR_TYPE_ID.eq(BAR_TYPE.ID)
.and(BAR_TYPE.NAME.equalIgnoreCase(barTypeName)))
.where(BAZ.SOMETHING_ID.eq(somethingId))
.and(BAZ.SOMETHING_ELSE_ID.eq(somethingElseId))
.and(bar.OTHER_ID.eq(otherId))
.orderBy(DSL.field("x.ord").desc())
.limit(1)
.fetchOneInto(BazId.class); //Wraps a UUID
}
- 绑定
public class FooIdsBinding extends DefaultBinding<Object[], FooId[]> {
private static final long serialVersionUID = 0L;
private static final UUIDConverter converter = new UUIDConverter();
public FooIdsBinding() {
super(new FooIdsConverter());
}
@Override
public void sql(final BindingSQLContext<FooId[]> ctx) {
final RenderContext render = ctx.render();
render.sql("array[");
final UUID[] uuids = ctx.convert(converter).value();
for (int i = 0, last = uuids.length - 1; i <= last; ++i) {
render.visit(DSL.val(uuids[i])).sql("::uuid");
if (i != last) {
render.sql(',');
}
}
render.sql("]::uuid[]");
}
@Override
public void register(final BindingRegisterContext<FooId[]> ctx) throws SQLException {
ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY);
}
static class BaseUUIDConverter {
public FooId[] from(final Object[] from) {
return from == null ? null : Arrays.stream(from)
.map(that -> new FooId((UUID)that))
.collect(Collectors.toList())
.toArray(new FooId[from.length]);
}
public UUID[] to(final FooId[] from) {
return from == null ? null : Arrays.stream(from)
.map(FooId::getUuid)
.collect(Collectors.toList())
.toArray(new UUID[from.length]);
}
public Class<FooId[]> toType() {
return FooId[].class;
}
}
private static class UUIDConverter extends BaseUUIDConverter implements Converter<UUID[], FooId[]> {
@Override
public FooId[] from(final UUID[] that) {
return super.from(that);
}
@Override
public Class<UUID[]> fromType() {
return UUID[].class;
}
}
}
- 转换器。必须是
Object[]
才能使生成的Table
编译
public class FooIdConverter extends FooIdsBinding.BaseUUIDConverter implements Converter<Object[],FooId[]> {
private static final long serialVersionUID = 0L;
@Override
public Class<Object[]> fromType() {
return (Class)UUID[].class;
}
}
- 有效的查询
db.insertInto(FOO)
.set(FOO.ID, new FooId())
.set(FOO.IDS, new FooId[]{new FooId(),new FooId()})
.execute();
- 不存在的查询
foo = new FooRecord();
foo.setId(new FooId());
foo.setIds(new FooId[]{new FooId(),new FooId()});
db.executeInsert(foo);
更新
我最终得到了这个绑定和转换器,它工作正常。我原以为我需要将数组的每个元素转换为一个 uuid,我的实现导致 jOOQ 的 sql 生成出现问题,但我认为我只在 jOOQ 覆盖之前看到与此相关的错误 Binding#register
为数组。
转换器
public class FooIdConverter implements Converter<Object[],FooId[]> { private static final long serialVersionUID = 1L; @Override public FooId[] from(final Object[] from) { return from == null ? null : Arrays.stream(from) .map(that -> new FooId((UUID)that)) .collect(Collectors.toList()) .toArray(new FooId[from.length]); } @Override public UUID[] to(final FooId[] from) { return from == null ? null : Arrays.stream(from) .map(FooId::getUuid) .collect(Collectors.toList()) .toArray(new UUID[from.length]); } @Override @SuppressWarnings("unchecked") public Class<Object[]> fromType() { return (Class)UUID[].class; } @Override public Class<FooId[]> toType() { return FooId[].class; } }
绑定
public class FooIdBinding extends DefaultBinding<Object[], FooId[]> { private static final long serialVersionUID = 1L; public FooIdBinding() { super(new FooIdConverter()); } @Override public void sql(final BindingSQLContext<FooId[]> ctx) { super.sql(ctx); ctx.render().sql("::uuid[]"); } @Override public void register(final BindingRegisterContext<FooId[]> ctx) throws SQLException { ctx.statement().registerOutParameter(ctx.index(), Types.ARRAY); } @Override public void get(final BindingGetResultSetContext<FooId[]> ctx) throws SQLException { ctx.value(_convert(ctx.resultSet().getArray(ctx.index()))); } @Override public void get(final BindingGetStatementContext<FooId[]> ctx) throws SQLException { ctx.value(_convert(ctx.statement().getArray(ctx.index()))); } @Override public void get(final BindingGetSQLInputContext<FooId[]> ctx) throws SQLException { ctx.value(_convert(ctx.input().readArray())); } @Override public void set(final BindingSetStatementContext<FooId[]> ctx) throws SQLException { final PreparedStatement ps = ctx.statement(); ps.setArray(ctx.index(), ps.getConnection().createArrayOf("uuid", ctx.value())); } @Override public void set(final BindingSetSQLOutputContext<FooId[]> ctx) throws SQLException { throw new UnsupportedOperationException(); } protected FooId[] _convert(final Array array) throws SQLException { if (array == null) { return null; } else { return converter().from(((UUID[]) array.getArray())); } } }
jOOQ 代码生成器中似乎存在阻止 UUID[]
类型被覆盖的错误:#4388