如何将PostgreSQL字符串迁移到文本类型?

时间:2013-01-24 23:30:02

标签: postgresql jpa

我们正在使用看起来像这样的JPA注释类型(groovy代码):

@Entity
@EqualsAndHashCode
class TextNote extends Serializable {
    @Id Long id
    String text
}

首次编写时,我是JPA的新手并首先编写SQL,然后使注释的类与SQL匹配。阅读PostgreSQL,似乎以下是我想要的表格:

CREATE TABLE textnote (
    id bigint NOT NULL,
    text text
);

这很有用,我们的表格看起来像这样:

 id  |          text
-----+------------------------
 837 | really long text here

我现在要做的是将JPA实体修改为:

@Entity
@EqualsAndHashCode
class TextNote extends Serializable {
    @Id Long id
    @Lob String text
}

通过添加@Lob注释,JPA提供程序(在我的情况下,hibernate)可以正确地为我生成DDL,以防我们想要交换数据库。它还准确记录了我想要的文本字段。现在,当一个笔记被创建时,我看到这样的东西:

 id  |          text
-----+------------------------
 837 | 33427

对于新笔记来说这很好,因为当我使用String getText()在代码中读取它时,它会返回非常长的文本。老实说我不知道​​PostgreSQL如何实现text类型,理论上我也不需要。但是,我们的生产数据库已经使用没有@Lob注释的旧代码存储了许多注释。在现有数据库上运行新代码会产生如下问题:

org.springframework.dao.DataIntegrityViolationException: Bad value for type long : not a number this time; SQL [n/a]; nested exception is org.hibernate.exception.DataException: Bad value for type long : not a number this time

对于现有笔记,SQL中是否有一种方法可以迁移旧笔记以正确使用@Lobtext类型?提前谢谢。

4 个答案:

答案 0 :(得分:12)

基于Postgres large object docs,您似乎必须将每个文本块写入文件并单独导入。这不是你应该在SQL中做的事情。

我对JPA一无所知,但@Lob与DDL或切换数据库有什么关系?你已经完全改变了列的类型; Postgres的text类型出了什么问题?


在此处关闭循环,以便在评论中不会丢失:

真正的问题是@Lob创建了Postgres text列,但Hibernate treats it创建了一个本地Postgres“大对象”,它将数据存储在其他地方并且只在表中留下一个oid(然后根据列类型存储为文本。这通常不是您想要的文字。

OP的解决方案是打击@Type(type="org.hibernate.type.StringClobType")以强制Hibernate存储常规文本。

Postgres通常不会将文本存储为五位整数。 :)

答案 1 :(得分:8)

我使用JPA来注释我的实体,并使用hibernate来持久保存它们。但我不想在我的实体中添加特定于hibernate的注释。因此,我有一个包含实体+注释的数据jar。然后在我的实现中,我指定persistence.xml并添加一个'CustomTypes.hbm.xml'。这是通过persistence.xml中的mapping-file标记自动扫描或添加的。

此映射包含我要从basictyperegistry覆盖的类型。 在这种情况下,使用materialized_clob类型。我不想要,因为当我使用查询工具浏览我的数据库时,我希望能够直接查看实际内容。 所以我补充说:

<typedef name="materialized_clob" class="org.hibernate.type.TextType" />

强制为所有clob使用指定的类型,而无需在我的数据包中添加特定的注释。

您可以通过在DEBUG级别上记录org.hibernate.type.BasicTypeRegistry来查看映射。

花了一些时间来解决这个问题,我希望这可以帮助任何面临同样问题的人。因为这可能也会解决你的问题,我认为可能值得在这里发布。

答案 2 :(得分:1)

我确信它已经很晚了,但对于今后遇到同样问题的人来说。

我还遇到了一个类似的问题,我在文本列中的旧数据直接在列中而不是作为OID。当我试图在升级后的应用程序中使用该数据时,我也得到了

Bad value for type long

要解决此问题,我创建了this script。可能它可以在将来帮助某人。

我从这篇文章获得了很大的帮助here

答案 3 :(得分:0)

如果使用spring,您可以创建LocalSessionFactoryBean后代以注入Hibernate类型覆盖。见例:

 public class CustomSessionFactoryBean extends LocalSessionFactoryBean {

    @Override
    protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
        // To store @Lob annotated Strings in TEXT fields.
        // By default for Postgres generated TEXT column, but stored OID of Postgres LOB object
        sfb.registerTypeOverride(new TextType() {
            @Override
            public String getName() {
                return StandardBasicTypes.MATERIALIZED_CLOB.getName();
            }
        });
        sfb.registerTypeOverride(new NTextType() {
            @Override
            public String getName() {
                return StandardBasicTypes.MATERIALIZED_NCLOB.getName();
            }
        });

        super.buildSessionFactory(sfb);
    }
 }

您不需要使用特定于Hibernate的注释@Type,只需使用@Lob注释String属性并使用CustomSessionFactoryBean