byte[] 的正确休眠注释

发布于 2024-09-18 13:58:37 字数 4270 浏览 10 评论 0 原文

我有一个使用 hibernate 3.1 和 JPA 注释的应用程序。它有一些带有 byte[] 属性的对象(大小为 1k - 200k)。它使用 JPA @Lob 注释,并且 hibernate 3.1 可以在所有主要数据库上很好地读取这些注释 —— 它似乎隐藏了 JDBC Blob 供应商的特性(正如它应该做的那样)。

@Entity
public class ConfigAttribute {
  @Lob
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
}

当我们发现 hibernate 3.5 破坏(并且无法修复) postgresql 中的这个注释组合(没有解决方法)。到目前为止,我还没有找到明确的修复方法,但我确实注意到,如果我只是删除 @Lob,它会使用 postgresql 类型 bytea (可以工作,但仅在 postgres 上)。

annotation                   postgres     oracle      works on
-------------------------------------------------------------
byte[] + @Lob                oid          blob        oracle
byte[]                       bytea        raw(255)    postgresql
byte[] + @Type(PBA)          oid          blob        oracle
byte[] + @Type(BT)           bytea        blob        postgresql

once you use @Type, @Lob seems to not be relevant
note: oracle seems to have deprecated the "raw" type since 8i.

我正在寻找一种方法来拥有一个可跨主要数据库移植的单个带注释的类(带有 blob 属性)。

  • 注释 byte[] 属性的可移植方法是什么?
  • 这个问题在最新版本的休眠中修复了吗?

更新: 阅读此博客后,我终于弄清楚了 JIRA 问题的原始解决方法是什么:显然你应该删除 @Lob 并将属性注释为:

@Type(type="org.hibernate.type.PrimitiveByteArrayBlobType") 
byte[] getValueBuffer() {...

但是,这对我不起作用 - 我仍然得到 OID 而不是 bytea;然而,它确实对 JIRA 问题的作者有用,他似乎想要 oid。

在 A. Garcia 回答之后,我尝试了这个组合,它实际上可以在 postgresql 上运行,但不能在 oracle 上运行。

@Type(type="org.hibernate.type.BinaryType") 
byte[] getValueBuffer() {...

我真正需要做的是控制哪个 @org.hibernate.annotations.Type 组合(@Lob + byte[] 被映射)到(在 postgresql 上)。


以下是来自 MaterializedBlobType(sql 类型 Blob)的 3.5.5.Final 的片段。根据 Steve 的博客,postgresql 希望你使用 Streams 作为 bytea(不要问我为什么),并使用 postgresql 的自定义 Blob 类型作为 oid。另请注意,在 JDBC 上使用 setBytes() 也适用于 bytea(根据过去的经验)。所以这解释了为什么 use-streams 没有影响它们都假设“bytea”。

public void set(PreparedStatement st, Object value, int index) {
 byte[] internalValue = toInternalFormat( value );
 if ( Environment.useStreamsForBinary() ) {
  // use streams = true
   st.setBinaryStream( index, 
    new ByteArrayInputStream( internalValue ), internalValue.length );
 }
 else {
  // use streams = false
  st.setBytes( index, internalValue );
 }
}

这会导致:

ERROR: column "signature" is of type oid but expression is of type bytea

更新 下一个逻辑问题是:“为什么不直接将表定义手动更改为 bytea”并保留 (@Lob + byte[])?这确实有效,直到您尝试存储空字节[]。 postgreSQL 驱动程序认为这是一个 OID 类型表达式,并且列类型是 bytea —— 这是因为 hibernate (正确地)调用 JDBC.setNull() 而不是 PG 驱动程序期望的 JDBC.setBytes(null) 。

ERROR: column "signature" is of type bytea but expression is of type oid

hibernate 中的类型系统当前是“正在进行中的工作”(根据 3.5.5 弃用注释)。事实上,3.5.5 的大部分代码已被弃用,很难知道在对 PostgreSQLDialect 进行子类化时该看什么)。

AFAKT,postgresql 上的 Types.BLOB/'oid' 应该映射到一些使用 OID 样式 JDBC 访问的自定义类型(即 PostgresqlBlobType 对象而不是 MaterializedBlobType)。我从未真正成功地将 Blob 与 postgresql 一起使用,但我确实知道 bytea 只是简单地工作/我期望的那样。

我目前正在查看 BatchUpdateException - 驱动程序可能不支持批处理。


2004 年的精彩引言: “总而言之,我想说的是,我们应该等待 JDBC 驱动程序正确执行 LOB,然后再更改 Hibernate。”

参考文献:

I have an application using hibernate 3.1 and JPA annotations. It has a few objects with byte[] attributes (1k - 200k in size). It uses the JPA @Lob annotation, and hibernate 3.1 can read these just fine on all major databases -- it seems to hide the JDBC Blob vendor peculiarities (as it should do).

@Entity
public class ConfigAttribute {
  @Lob
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
}

We had to upgrade to 3.5, when we discovered that hibernate 3.5 breaks (and won't fix) this annotation combination in postgresql (with no workaround). I have not found a clear fix so far, but I did notice that if I just remove the @Lob, it uses the postgresql type bytea (which works, but only on postgres).

annotation                   postgres     oracle      works on
-------------------------------------------------------------
byte[] + @Lob                oid          blob        oracle
byte[]                       bytea        raw(255)    postgresql
byte[] + @Type(PBA)          oid          blob        oracle
byte[] + @Type(BT)           bytea        blob        postgresql

once you use @Type, @Lob seems to not be relevant
note: oracle seems to have deprecated the "raw" type since 8i.

I am looking for a way to have a single annotated class (with a blob property) which is portable across major databases.

  • What is the portable way to annotate a byte[] property?
  • Is this fixed in some recent version of hibernate?

Update:
After reading this blog I have finally figured out what the original workaround in the JIRA issue was: Apparently you are supposed to drop @Lob and annotate the property as:

@Type(type="org.hibernate.type.PrimitiveByteArrayBlobType") 
byte[] getValueBuffer() {...

However, this does not work for me -- I still get OIDs instead of bytea; it did however work for the author of the JIRA issue, who seemed to want oid.

After the answer from A. Garcia, I then tried this combo, which actually does work on postgresql, but not on oracle.

@Type(type="org.hibernate.type.BinaryType") 
byte[] getValueBuffer() {...

What I really need to do is control which @org.hibernate.annotations.Type the combination (@Lob + byte[] gets mapped) to (on postgresql).


Here is the snippet from 3.5.5.Final from MaterializedBlobType (sql type Blob). According to Steve's blog, postgresql wants you to use Streams for bytea (don't ask me why) and postgresql's custom Blob type for oids. Note also that using setBytes() on JDBC is also for bytea (from past experience). So this explains why use-streams has no affect they both assume 'bytea'.

public void set(PreparedStatement st, Object value, int index) {
 byte[] internalValue = toInternalFormat( value );
 if ( Environment.useStreamsForBinary() ) {
  // use streams = true
   st.setBinaryStream( index, 
    new ByteArrayInputStream( internalValue ), internalValue.length );
 }
 else {
  // use streams = false
  st.setBytes( index, internalValue );
 }
}

This results in:

ERROR: column "signature" is of type oid but expression is of type bytea

Update
The next logical question is: "why not just change the table definitions manually to bytea" and keep the (@Lob + byte[])? This does work, UNTIL you try to store a null byte[]. Which the postgreSQL driver thinks is an OID type expression and the column type is bytea -- this is because hibernate (rightly) calls JDBC.setNull() instead of JDBC.setBytes(null) which PG driver expects.

ERROR: column "signature" is of type bytea but expression is of type oid

The type system in hibernate is currently a 'work in progress' (according to 3.5.5 deprecation comment). In fact so much of the 3.5.5 code is deprecated, it is hard to know what to look at when sub-classing the PostgreSQLDialect).

AFAKT, Types.BLOB/'oid' on postgresql should be mapped to some custom type which uses OID style JDBC access (i.e. PostgresqlBlobType object and NOT MaterializedBlobType). I've never actually successfully used Blobs with postgresql, but I do know that bytea just simply works as one / I would expect.

I am currently looking at the BatchUpdateException -- its possible that the driver doesn't support batching.


Great quote from 2004:
"To sum up my ramblings, I'd say they we should wait for the JDBC driver to do LOBs properly before changing Hibernate."

References:

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

記憶穿過時間隧道 2024-09-25 13:58:38

Hibernate 6+、javaee 9+

  @Lob
  @JdbcTypeCode(Types.BINARY)
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }

Hibernate 6+, javaee 9+

  @Lob
  @JdbcTypeCode(Types.BINARY)
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
伪装你 2024-09-25 13:58:38

在 Postgres 上,@Lob 会破坏 byte[],因为它试图将其保存为 oid,对于 String 也会出现同样的问题。下面的代码在 postgres 上被破坏,而 postgres 在 oracle 上运行得很好。

@Lob
private String stringField;

@Lob
private byte[]   someByteStream;

为了解决上面的问题

public class PostgreSQLDialectCustom extends PostgreSQL82Dialect{

public PostgreSQLDialectCustom()
{
    super();
    registerColumnType(Types.BLOB, "bytea");
}

 @Override
 public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (Types.CLOB == sqlTypeDescriptor.getSqlType()) {
      return LongVarcharTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
}

在 postgres 上写了下面的自定义 hibernate.dialect现在在 hibernate XYZ 中配置自定义方言

hibernate.dialect=X.Y.Z.PostgreSQLDialectCustom   

是包名称。

现在工作正常了。
注意-我的 Hibernate 版本 - 5.2.8.Final
Postgres 版本 - 9.6.3

On Postgres @Lob is breaking for byte[] as it tries to save it as oid, and for String also same problem occurs. Below code is breaking on postgres which is working fine on oracle.

@Lob
private String stringField;

and

@Lob
private byte[]   someByteStream;

In order to fix above on postgres have written below custom hibernate.dialect

public class PostgreSQLDialectCustom extends PostgreSQL82Dialect{

public PostgreSQLDialectCustom()
{
    super();
    registerColumnType(Types.BLOB, "bytea");
}

 @Override
 public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (Types.CLOB == sqlTypeDescriptor.getSqlType()) {
      return LongVarcharTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
}

Now configure custom dialect in hibernate

hibernate.dialect=X.Y.Z.PostgreSQLDialectCustom   

X.Y.Z is package name.

Now it working fine.
NOTE- My Hibernate version - 5.2.8.Final
Postgres version- 9.6.3

西瑶 2024-09-25 13:58:38
  1. 您可以在实体中使用
@Lob
@Type(type = "org.hibernate.type.BinaryType")
@Column(name = "stringField")
private byte[] stringField;
  1. You can use in the entity
@Lob
@Type(type = "org.hibernate.type.BinaryType")
@Column(name = "stringField")
private byte[] stringField;
彩扇题诗 2024-09-25 13:58:38

我通过使用 Postgres 的 XML 文件覆盖注释来实现它。为 Oracle 保留注释。在我看来,在这种情况下,我们最好使用 xml 映射来覆盖这个麻烦实体的映射。我们可以使用 xml 映射覆盖单个/多个实体。因此,我们将为主要支持的数据库使用注释,并为每个其他数据库使用 xml 文件。

注意:我们只需要重写一个类,所以这并不是什么大问题。
从我的例子中阅读更多内容
使用 XML 覆盖注释的示例

I got it work by overriding annotation with XML file for Postgres. Annotation is kept for Oracle. In my opinion, in this case it would be best we override the mapping of this trouble-some enity with xml mapping. We can override single / multiple entities with xml mapping. So we would use annotation for our mainly-supported database, and a xml file for each other database.

Note: we just need to override one single class , so it is not a big deal.
Read more from my example
Example to override annotation with XML

茶花眉 2024-09-25 13:58:38

感谢贾斯汀、帕斯卡引导我走向正确的方向。我在使用 Hibernate 3.5.3 时也遇到了同样的问题。您的研究和对正确课程的指导帮助我识别了问题并进行了修复。

为了让那些仍然坚持使用 Hibernate 3.5 并使用 oid + byte[] + @LoB 组合的人受益,以下是我为解决该问题所做的工作。

  1. 我创建了一个自定义 BlobType,它扩展了 MaterializedBlobType,并使用 oid 样式访问覆盖了 set 和 get 方法。

    公共类 CustomBlobType 扩展 MaterializedBlobType {
    
    私有静态最终字符串 POSTGRESQL_DIALECT = PostgreSQLDialect.class.getName();
    
    /**
     * 当前设置的方言。
     */
    私有字符串方言 = hibernateConfiguration.getProperty(Environment.DIALECT);
    
    /*
     *(非 Javadoc)
     * @see org.hibernate.type.AbstractBynaryType#set(java.sql.PreparedStatement, java.lang.Object, int)
     */
    @覆盖
    公共无效集(PreparedStatement st,对象值,int索引)抛出HibernateException,SQLException {
        字节 [] 内部值 = toInternalFormat(值);
    
        如果 (POSTGRESQL_DIALECT.equals(方言)) {
            尝试 {
    
    //我可以通过自定义sessionFactory包装器访问sessionFactory。
    st.setBlob(index, Hibernate.createBlob(internalValue, sessionFactory.getCurrentSession()));
                } catch (SystemException e) {
                    抛出新的 HibernateException(e);
                }
            } 别的 {
                st.setBytes(索引,internalValue);
            }
        }
    
    /*
     *(非 Javadoc)
     * @see org.hibernate.type.AbstractBynaryType#get(java.sql.ResultSet, java.lang.String)
     */
    @覆盖
    public Object get(ResultSet rs, String name) 抛出 HibernateException, SQLException {
        Blob blob = rs.getBlob(name);
        如果 (rs.wasNull()) {
            返回空值;
        }
        int 长度 = (int) blob.length();
        return toExternalFormat(blob.getBytes(1, length));
      }
    }
    
  2. 向 Hibernate 注册 CustomBlobType。以下是我为实现这一目标所做的事情。

    hibernateConfiguration= new AnnotationConfiguration();
    映射映射= hibernateConfiguration.createMappings();
    映射.addTypeDef("materialized_blob", "xyzBlobType", null);
    

Thanks Justin, Pascal for guiding me to the right direction. I was also facing the same issue with Hibernate 3.5.3. Your research and pointers to the right classes had helped me identify the issue and do a fix.

For the benefit for those who are still stuck with Hibernate 3.5 and using oid + byte[] + @LoB combination, following is what I have done to fix the issue.

  1. I created a custom BlobType extending MaterializedBlobType and overriding the set and the get methods with the oid style access.

    public class CustomBlobType extends MaterializedBlobType {
    
    private static final String POSTGRESQL_DIALECT = PostgreSQLDialect.class.getName();
    
    /**
     * Currently set dialect.
     */
    private String dialect = hibernateConfiguration.getProperty(Environment.DIALECT);
    
    /*
     * (non-Javadoc)
     * @see org.hibernate.type.AbstractBynaryType#set(java.sql.PreparedStatement, java.lang.Object, int)
     */
    @Override
    public void set(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
        byte[] internalValue = toInternalFormat(value);
    
        if (POSTGRESQL_DIALECT.equals(dialect)) {
            try {
    
    //I had access to sessionFactory through a custom sessionFactory wrapper.
    st.setBlob(index, Hibernate.createBlob(internalValue, sessionFactory.getCurrentSession()));
                } catch (SystemException e) {
                    throw new HibernateException(e);
                }
            } else {
                st.setBytes(index, internalValue);
            }
        }
    
    /*
     * (non-Javadoc)
     * @see org.hibernate.type.AbstractBynaryType#get(java.sql.ResultSet, java.lang.String)
     */
    @Override
    public Object get(ResultSet rs, String name) throws HibernateException, SQLException {
        Blob blob = rs.getBlob(name);
        if (rs.wasNull()) {
            return null;
        }
        int length = (int) blob.length();
        return toExternalFormat(blob.getBytes(1, length));
      }
    }
    
    1. Register the CustomBlobType with Hibernate. Following is what i did to achieve that.

      hibernateConfiguration= new AnnotationConfiguration();
      Mappings mappings = hibernateConfiguration.createMappings();
      mappings.addTypeDef("materialized_blob", "x.y.z.BlobType", null);
      
探春 2024-09-25 13:58:37

注释 byte[] 属性的可移植方法是什么?

这取决于你想要什么。 JPA 可以持久保存未注释的byte[]。来自 JPA 2.0 规范:

11.1.6 基本注解

Basic 注释是最简单的
映射到数据库列的类型。
可以应用Basic注释
到持久属性或实例
以下任一变量
类型:Java 原语、类型、包装器
的原始类型,
java.lang.String,
java.math.BigInteger,
java.math.BigDecimal,
java.util.Date,
java.util.Calendar、java.sql.Date、
java.sql.Timejava.sql.Timestamp
字节[]字节[]char[]字符[] 、枚举和任何其他
实现可序列化的类型。
如第 2.8 节所述,使用
Basic 注释是可选的
对于持久字段和属性
这些类型。如果基本
没有为这样的指定注释
字段或属性,默认值
基本注释的部分将适用。

Hibernate 会将其“默认”映射到 PostgreSQL 处理的 SQL VARBINARY(或 SQL LONGVARBINARY,具体取决于 Column 大小?)与bytea

但如果您希望将byte[]存储在大对象中,则应该使用@Lob。从规格来看:

11.1.24 Lob 注释

Lob 注释指定
持久属性或字段应该是
作为一个大对象持续存在
数据库支持的大对象类型。
便携式应用程序应使用
映射到 a 时的 Lob 注释
数据库 Lob 类型。 Lob 注释
可以与
基本注释或与
ElementCollection 注释时
元素收藏价值基本
类型。 Lob 可以是二进制文件或
字符类型。 Lob 类型是
从类型推断出
持久字段或属性,
除了字符串和字符类型之外,
默认为 Blob。

Hibernate 会将其映射到 PostgreSQL 使用 oid 处理的 SQL BLOB

这个问题在最新版本的 hibernate 中修复了吗?

嗯,问题是我不知道问题到底是什么。但我至少可以说,自 3.5.0-Beta-2(这是引入更改的地方)以来,3.5.x 分支中没有任何变化。

但我对诸如 HHH-4876HHH-4617

hibernate.jdbc.use_streams_for_binary=false

PostgreSQLAndBLOBs" rel="noreferrer">PostgreSQL 和 BLOBs (在 PostgreSQLDialect 的 javadoc 中提到)是,如果你想使用 oid, 即 byte[]@Lob (这是我的理解,因为 VARBINARY 不是您想要的 Oracle)。你尝试过这个吗?

作为替代方案,HHH-4876 建议使用已弃用的 PrimitiveByteArrayBlobType 以获取旧行为(Hibernate 3.5 之前的版本)。

参考

  • JPA 2.0 规范
    • 第 2.8 节“非关系字段或属性的映射默认值”
    • 第 11.1.6 节“基本注释”
    • 第 11.1.24 节“Lob 注释”

资源

What is the portable way to annotate a byte[] property?

It depends on what you want. JPA can persist a non annotated byte[]. From the JPA 2.0 spec:

11.1.6 Basic Annotation

The Basic annotation is the simplest
type of mapping to a database column.
The Basic annotation can be applied
to a persistent property or instance
variable of any of the following
types: Java primitive, types, wrappers
of the primitive types,
java.lang.String,
java.math.BigInteger,
java.math.BigDecimal,
java.util.Date,
java.util.Calendar, java.sql.Date,
java.sql.Time, java.sql.Timestamp,
byte[], Byte[], char[], Character[], enums, and any other
type that implements Serializable.
As described in Section 2.8, the use
of the Basic annotation is optional
for persistent fields and properties
of these types. If the Basic
annotation is not specified for such a
field or property, the default values
of the Basic annotation will apply.

And Hibernate will map a it "by default" to a SQL VARBINARY (or a SQL LONGVARBINARY depending on the Column size?) that PostgreSQL handles with a bytea.

But if you want the byte[] to be stored in a Large Object, you should use a @Lob. From the spec:

11.1.24 Lob Annotation

A Lob annotation specifies that a
persistent property or field should be
persisted as a large object to a
database-supported large object type.
Portable applications should use the
Lob annotation when mapping to a
database Lob type. The Lob annotation
may be used in conjunction with the
Basic annotation or with the
ElementCollection annotation when the
element collection value is of basic
type. A Lob may be either a binary or
character type. The Lob type is
inferred from the type of the
persistent field or property and,
except for string and character types,
defaults to Blob.

And Hibernate will map it to a SQL BLOB that PostgreSQL handles with a oid
.

Is this fixed in some recent version of hibernate?

Well, the problem is that I don't know what the problem is exactly. But I can at least say that nothing has changed since 3.5.0-Beta-2 (which is where a changed has been introduced)in the 3.5.x branch.

But my understanding of issues like HHH-4876, HHH-4617 and of PostgreSQL and BLOBs (mentioned in the javadoc of the PostgreSQLDialect) is that you are supposed to set the following property

hibernate.jdbc.use_streams_for_binary=false

if you want to use oid i.e. byte[] with @Lob (which is my understanding since VARBINARY is not what you want with Oracle). Did you try this?

As an alternative, HHH-4876 suggests using the deprecated PrimitiveByteArrayBlobType to get the old behavior (pre Hibernate 3.5).

References

  • JPA 2.0 Specification
    • Section 2.8 "Mapping Defaults for Non-Relationship Fields or Properties"
    • Section 11.1.6 "Basic Annotation"
    • Section 11.1.24 "Lob Annotation"

Resources

遇见了你 2024-09-25 13:58:37

O'reilly Enterprise JavaBeans 3.0 的内容如下

对于这些非常大的对象,JDBC 有特殊类型。 java.sql.Blob类型表示二进制数据,java.sql.Clob表示字符数据。

这里是 PostgreSQLDialect 源代码

public PostgreSQLDialect() {
    super();
    ...
    registerColumnType(Types.VARBINARY, "bytea");
    /**
      * Notice it maps java.sql.Types.BLOB as oid
      */
    registerColumnType(Types.BLOB, "oid");
}

那么你可以做什么

覆盖 PostgreSQLDialect 如下

public class CustomPostgreSQLDialect extends PostgreSQLDialect {

    public CustomPostgreSQLDialect() {
        super();

        registerColumnType(Types.BLOB, "bytea");
    }
}

现在只需定义你的自定义方言

<property name="hibernate.dialect" value="br.com.ar.dialect.CustomPostgreSQLDialect"/>

并使用你的便携式 JPA @Lob 注释

@Lob
public byte[] getValueBuffer() {

UPDATE

这里已提取 此处

我有一个在 hibernate 3.3.2 中运行的应用程序,并且该应用程序工作正常,所有 blob 字段都使用 oid(java 中的 byte[])

...

迁移到hibernate 3.5,所有 blob 字段不再工作,服务器日志显示:错误 org.hibernate.util.JDBCExceptionReporter - 错误:列的类型为 oid,但表达式的类型为 bytea< /p>

which可以在此处进行解释

这通常不是 PG JDBC 中的错误而是 3.5 版本中 Hibernate 默认实现的更改。在我的情况下设置连接上的兼容属性
没有帮助

...

更多的是我在 3.5 - beta 2 中看到的,我不知道这是否已修复是 Hibernate - 没有 @Type 注释 - 将自动创建 oid 类型的列,但会尝试阅读此内容作为字节茶

有趣的是,当他将 Types.BOLB 映射为 bytea 时(请参阅 CustomPostgreSQLDialect),他得到

无法执行 JDBC 批量更新

插入或更新时

Here goes what O'reilly Enterprise JavaBeans, 3.0 says

JDBC has special types for these very large objects. The java.sql.Blob type represents binary data, and java.sql.Clob represents character data.

Here goes PostgreSQLDialect source code

public PostgreSQLDialect() {
    super();
    ...
    registerColumnType(Types.VARBINARY, "bytea");
    /**
      * Notice it maps java.sql.Types.BLOB as oid
      */
    registerColumnType(Types.BLOB, "oid");
}

So what you can do

Override PostgreSQLDialect as follows

public class CustomPostgreSQLDialect extends PostgreSQLDialect {

    public CustomPostgreSQLDialect() {
        super();

        registerColumnType(Types.BLOB, "bytea");
    }
}

Now just define your custom dialect

<property name="hibernate.dialect" value="br.com.ar.dialect.CustomPostgreSQLDialect"/>

And use your portable JPA @Lob annotation

@Lob
public byte[] getValueBuffer() {

UPDATE

Here has been extracted here

I have an application running in hibernate 3.3.2 and the applications works fine, with all blob fields using oid (byte[] in java)

...

Migrating to hibernate 3.5 all blob fields not work anymore, and the server log shows: ERROR org.hibernate.util.JDBCExceptionReporter - ERROR: column is of type oid but expression is of type bytea

which can be explained here

This generaly is not bug in PG JDBC, but change of default implementation of Hibernate in 3.5 version. In my situation setting compatible property on connection
did not helped
.

...

Much more this what I saw in 3.5 - beta 2, and i do not know if this was fixed is Hibernate - without @Type annotation - will auto-create column of type oid, but will try to read this as bytea

Interesting is because when he maps Types.BOLB as bytea (See CustomPostgreSQLDialect) He get

Could not execute JDBC batch update

when inserting or updating

巴黎夜雨 2024-09-25 13:58:37

我正在使用 Hibernate 4.2.7.SP1 和 Postgres 9.3,以下内容对我有用:

@Entity
public class ConfigAttribute {
  @Lob
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
}

因为 Oracle 对此没有任何问题,对于 Postgres,我使用自定义方言:

public class PostgreSQLDialectCustom extends PostgreSQL82Dialect {

    @Override
    public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) {
      return BinaryTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
}

我认为这个解决方案的优点是,我可以保留休眠罐子未受影响。

有关 Hibernate 的更多 Postgres/Oracle 兼容性问题,请参阅我的 博客文章

I'm using the Hibernate 4.2.7.SP1 with Postgres 9.3 and following works for me:

@Entity
public class ConfigAttribute {
  @Lob
  public byte[] getValueBuffer() {
    return m_valueBuffer;
  }
}

as Oracle has no trouble with that, and for Postgres I'm using custom dialect:

public class PostgreSQLDialectCustom extends PostgreSQL82Dialect {

    @Override
    public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) {
      return BinaryTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
}

the advantage of this solution I consider, that I can keep hibernate jars untouched.

For more Postgres/Oracle compatibility issues with Hibernate, see my blog post.

你怎么这么可爱啊 2024-09-25 13:58:37

我终于成功了。然而,它扩展了 A. Garcia 的解决方案,因为问题在于 hibernate 类型 MaterializedBlob 类型仅映射 Blob > 。 bytea 还不够,我们需要替代 MaterializedBlobType,它可以与 hibernates 损坏的 blob 支持一起使用。该实现仅适用于 bytea,但也许 JIRA 问题中想要 OID 的人可以贡献一个 OID 实现。

遗憾的是,在运行时替换这些类型是一件痛苦的事情,因为它们应该是方言的一部分。
如果这个 JIRA 增强进入 3.6 就有可能。

public class PostgresqlMateralizedBlobType extends AbstractSingleColumnStandardBasicType<byte[]> {
 public static final PostgresqlMateralizedBlobType INSTANCE = new PostgresqlMateralizedBlobType();

 public PostgresqlMateralizedBlobType() {
  super( PostgresqlBlobTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE );
 }

  public String getName() {
   return "materialized_blob";
  }
}

其中大部分可能是静态的(getBinder() 真的需要一个新实例吗?),但我不太了解 hibernate 内部,所以这主要是复制+粘贴+修改。

public class PostgresqlBlobTypeDescriptor extends BlobTypeDescriptor implements SqlTypeDescriptor {
  public static final BlobTypeDescriptor INSTANCE = new PostgresqlBlobTypeDescriptor();

  public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
   return new PostgresqlBlobBinder<X>(javaTypeDescriptor, this);
  }
  public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
   return new BasicExtractor<X>( javaTypeDescriptor, this ) {
    protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { 
      return (X)rs.getBytes(name);
    }
   };
  }
}

public class PostgresqlBlobBinder<J> implements ValueBinder<J> {
 private final JavaTypeDescriptor<J> javaDescriptor;
 private final SqlTypeDescriptor sqlDescriptor;

 public PostgresqlBlobBinder(JavaTypeDescriptor<J> javaDescriptor, SqlTypeDescriptor sqlDescriptor) { 
  this.javaDescriptor = javaDescriptor; this.sqlDescriptor = sqlDescriptor;
 }  
 ...
 public final void bind(PreparedStatement st, J value, int index, WrapperOptions options) 
 throws SQLException {
  st.setBytes(index, (byte[])value);
 }
}

I have finally got this working. It expands on the solution from A. Garcia, however, since the problem lies in the hibernate type MaterializedBlob type just mapping Blob > bytea is not sufficient, we need a replacement for MaterializedBlobType which works with hibernates broken blob support. This implementation only works with bytea, but maybe the guy from the JIRA issue who wanted OID could contribute an OID implementation.

Sadly replacing these types at runtime is a pain, since they should be part of the Dialect.
If only this JIRA enhanement gets into 3.6 it would be possible.

public class PostgresqlMateralizedBlobType extends AbstractSingleColumnStandardBasicType<byte[]> {
 public static final PostgresqlMateralizedBlobType INSTANCE = new PostgresqlMateralizedBlobType();

 public PostgresqlMateralizedBlobType() {
  super( PostgresqlBlobTypeDescriptor.INSTANCE, PrimitiveByteArrayTypeDescriptor.INSTANCE );
 }

  public String getName() {
   return "materialized_blob";
  }
}

Much of this could probably be static (does getBinder() really need a new instance?), but I don't really understand the hibernate internal so this is mostly copy + paste + modify.

public class PostgresqlBlobTypeDescriptor extends BlobTypeDescriptor implements SqlTypeDescriptor {
  public static final BlobTypeDescriptor INSTANCE = new PostgresqlBlobTypeDescriptor();

  public <X> ValueBinder<X> getBinder(final JavaTypeDescriptor<X> javaTypeDescriptor) {
   return new PostgresqlBlobBinder<X>(javaTypeDescriptor, this);
  }
  public <X> ValueExtractor<X> getExtractor(final JavaTypeDescriptor<X> javaTypeDescriptor) {
   return new BasicExtractor<X>( javaTypeDescriptor, this ) {
    protected X doExtract(ResultSet rs, String name, WrapperOptions options) throws SQLException { 
      return (X)rs.getBytes(name);
    }
   };
  }
}

public class PostgresqlBlobBinder<J> implements ValueBinder<J> {
 private final JavaTypeDescriptor<J> javaDescriptor;
 private final SqlTypeDescriptor sqlDescriptor;

 public PostgresqlBlobBinder(JavaTypeDescriptor<J> javaDescriptor, SqlTypeDescriptor sqlDescriptor) { 
  this.javaDescriptor = javaDescriptor; this.sqlDescriptor = sqlDescriptor;
 }  
 ...
 public final void bind(PreparedStatement st, J value, int index, WrapperOptions options) 
 throws SQLException {
  st.setBytes(index, (byte[])value);
 }
}
嘿看小鸭子会跑 2024-09-25 13:58:37

我通过添加 @Lob 注释解决了我的问题,该注释将在 oracle 中创建 byte[] 作为 blob ,但此注释会将字段创建为 oid ,而无法正常工作,为了使 byte[] 创建为 bytea 我创建了客户方言postgres 如下

Public class PostgreSQLDialectCustom extends PostgreSQL82Dialect {
    public PostgreSQLDialectCustom() {
        System.out.println("Init PostgreSQLDialectCustom");
        registerColumnType( Types.BLOB, "bytea" );

      }

    @Override
    public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) {
      return BinaryTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
 }

还需要覆盖方言

spring.jpa.properties.hibernate.dialect=com.ntg.common.DBCompatibilityHelper.PostgreSQLDialectCustom

的参数,可以找到更多提示: https://dzone.com/articles/postgres-and-oracle

i fixed My issue by adding the annotation of @Lob which will create the byte[] in oracle as blob , but this annotation will create the field as oid which not work properly , To make byte[] created as bytea i made customer Dialect for postgres as below

Public class PostgreSQLDialectCustom extends PostgreSQL82Dialect {
    public PostgreSQLDialectCustom() {
        System.out.println("Init PostgreSQLDialectCustom");
        registerColumnType( Types.BLOB, "bytea" );

      }

    @Override
    public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) {
    if (sqlTypeDescriptor.getSqlType() == java.sql.Types.BLOB) {
      return BinaryTypeDescriptor.INSTANCE;
    }
    return super.remapSqlTypeDescriptor(sqlTypeDescriptor);
  }
 }

Also need to override parameter for the Dialect

spring.jpa.properties.hibernate.dialect=com.ntg.common.DBCompatibilityHelper.PostgreSQLDialectCustom

more hint can be found her : https://dzone.com/articles/postgres-and-oracle

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文