为byte []设置适当的休眠注释

2020/11/25 07:52 · java ·  · 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;
  }
}

当我们发现休眠3.5破坏了(并且不会修复) postgresql中的这个注释组合时(没有解决方法),我们不得不升级到3.5 到目前为止,我还没有找到明确的修复程序,但是我确实注意到,如果我只是删除@Lob,它将使用postteasql类型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希望您将流用于bytea(不要问我为什么),并将postgresql的自定义Blob类型用于oid。还要注意,在JDBC上使用setBytes()也可用于bytea(根据过去的经验)。因此,这解释了为什么使用流对它们都假定为“ 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 [])?
确实工作,UNTIL你尝试存储空字节[]。postgreSQL驱动程序认为是OID类型表达式,列类型是bytea,这是因为休眠(正确地)调用JDBC.setNull()而不是PG驱动程序期望的JDBC.setBytes(null)。

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

休眠中的类型系统当前是一个“进行中的工作”(根据3.5.5弃用注释)。实际上,不赞成使用3.5.5的很多代码,很难知道在对PostgreSQLDialect进行子类化时要看什么。

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

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


2004年的一句名言:“总结一下,我会说,在更改Hibernate之前,我们应该等待JDBC驱动程序正确执行LOB。”

参考文献:

注释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.Calendarjava.sql.Date
java.sql.Timejava.sql.Timestamp
byte[]Byte[]char[]Character[],枚举,以及任何其他类型的器具Serializable如2.8节所述,Basic对于这些类型的持久字段和属性,注释的使用是可选的。如果未为此类字段或属性指定基本注释,则将应用基本注释的默认值。

并且Hibernate会将它“默认”映射到PostgreSQL使用PostgreSQL处理的SQL VARBINARY(或LONGVARBINARY取决于Column大小的SQL ?)bytea

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

11.1.24吊球注释

Lob注释指定持久性属性或字段应保持为一个大型对象到数据库支持的大对象类型。可移植应用程序Lob在映射到数据库Lob类型时应使用
注释所述
Lob注释可以与基本注释或与一起使用
ElementCollection的注释当元素集合值是基本类型。ALob可以是二进制或字符类型。Lob类型是从持久字段或属性类型推断出来的,并且除了字符串和字符类型外,默认为Blob。

并且Hibernate会将其映射到BLOBPostgreSQL使用PostgreSQL处理的SQL oid

在某些最新的休眠版本中已解决此问题吗?

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

但是,我对HHH-4876HHH-4617以及PostgreSQL和BLOB(在的javadoc中提到 PostgreSQLDialect等问题的理解是,您应该设置以下属性

hibernate.jdbc.use_streams_for_binary=false

如果您想使用oidiebyte[]一起使用@Lob(这是我的理解,因为VARBINARY这不是您想要的Oracle)。你有尝试过吗?

作为替代方案,HHH-4876建议使用不推荐使用的方法PrimitiveByteArrayBlobType来获得旧的行为(Hibernate 3.5之前的版本)。

参考文献

  • JPA 2.0规范

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

资源资源

这就是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() {

更新

这里已经提取这里

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

...

迁移到休眠3.5以后,所有blob字段都不再起作用,并且服务器日志显示:错误org.hibernate.util.JDBCExceptionReporter-错误:列的类型为oid,但表达式的类型为bytea

可以在 这里解释

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

...

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

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

无法执行JDBC批量更新

插入或更新时

我正在将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的更多兼容性问题,请参阅我的博客文章

我终于完成了这项工作。它扩展了A. Garcia的解决方案,但是,由于问题出在休眠类型MaterializedBlob类型上,仅映射Blob> bytea是不够的,因此我们需要替换MaterializedBlobType,它可以与休眠破碎的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()确实需要一个新实例吗?),但是我不太了解休眠内部,因此这主要是复制+粘贴+修改。

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);
 }
}

我通过添加@Lob注释来解决我的问题,该注释将在oracle中将blob创建为byte [],但此注释会将字段创建为无法正常工作的oid。 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

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

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

在Postgres上,@ Lob试图将byte []保存为oid时出错了,而对于String来说,也会发生相同的问题。下面的代码在postgres上中断,在oracle上运行正常。

@Lob
private String stringField;

@Lob
private byte[]   someByteStream;

为了解决以上问题,在postgres上编写了以下自定义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);
  }
}

现在在休眠模式下配置自定义方言

hibernate.dialect=X.Y.Z.PostgreSQLDialectCustom   

XYZ是程序包名称。

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

感谢贾斯汀,帕斯卡(Pascal)指导我朝着正确的方向前进。我还面临着与Hibernate 3.5.3相同的问题。您的研究和对正确班级的指导帮助我确定了问题并进行了修复。

为了使仍然使用Hibernate 3.5并使用oid + byte [] + @LoB组合的用户受益,以下是我为解决此问题所做的工作。

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

    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. 向Hibernate注册CustomBlobType。以下是我为实现这一目标所做的工作。

      hibernateConfiguration= new AnnotationConfiguration();
      Mappings mappings = hibernateConfiguration.createMappings();
      mappings.addTypeDef("materialized_blob", "x.y.z.BlobType", null);
      
本文地址:http://java.askforanswer.com/weibyte-shezhishidangdexiumianzhushi.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!