【问题标题】:JPA: "Data too long for column" does not changeJPA:“列的数据太长”不会改变
【发布时间】:2012-09-06 05:16:55
【问题描述】:

我的一个实体Machinery 有一个名为notesString 属性。 JPA 2-Hibernate 为我生成架构,在我的例子中,RDBMS 是 MySQL。

notes 被创建为VARCHAR(255) 列,这是正确的。

用户开始创建记录并且一切正常,但随后一些用户收到了臭名昭著的Data too long for column "notes" 错误。

该字段没有足够的空间容纳用户的机械笔记!好的,没问题。让我们更改架构!

所以,我打开我的实体类并将我的属性更改为:

@Column(length=1000000)
@Lob
private String notes;

顺便说一句,我的persistence.xml 声明:

<property name="hibernate.hbm2ddl.auto" value="update" />

应用程序重新启动后,我很高兴 Hibernate 将我的 notes 列更改为 LONGTEXT(这对我来说已经足够了)。

所以我首先尝试使用我的应用程序创建一个新的“长期记录”记录,但我仍然出现错误“数据太长”,尽管现在是 LONGTEXT

然后,我尝试从 MySQL 命令行执行原始的INSERT,它可以正常工作!我可以在该字段中插入长注释!

最后,我 DROP 我的本地/暂存数据库架构并将 persistence.xml 中的 hibernate.hbm2ddl.auto 更改为 create 并且它可以工作。

JPA 是否仍然认为它是 VARCHAR?它是否有某种缓存或存储架构信息的地方?

显然,我不能放弃我的生产数据库。那么,如何重置或更改列类型?

我正在使用 JBossAS7 JPA 2-Hibernate。

【问题讨论】:

    标签: hibernate jpa longtext


    【解决方案1】:

    权威的 Hibernate 书籍“Java 持久性与 Hibernate”提到了这一点,关于 hibernate.hbm2ddl.autoupdate 值(粗体是我的)

    此配置属性的附加选项更新可以是 在开发过程中很有用:它启用了内置的 SchemaUpdate 工具, 这可以使模式演变更容易。如果启用,休眠读取 启动时的 JDBC 数据库元数据并创建新表和 通过将旧模式与当前映射进行比较来约束 元数据。 请注意,此功能取决于 由 JDBC 驱动程序提供的元数据,该区域包含许多驱动程序 缺乏。因此,在实践中,此功能不那么令人兴奋,并且 比听起来有用。

    Hibernate 文档也建议here

    SchemaUpdate 工具将使用 “增量”变化。 SchemaUpdate 依赖于 JDBC 元数据 API,因此不适用于所有 JDBC 驱动程序。

    我尝试复制您的用例,发现我也遇到了这个问题令人惊讶。

    我有一个这样的用户实体

    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.Id;
    import javax.persistence.Lob;
    import javax.persistence.NamedQuery;
    import javax.persistence.Table;
    
    @Entity
    @Table( name = "usr" )
    
    public class User {
      @Id
      @GeneratedValue
      private Long id;
    
      @Column( length = 40, unique = true )
      private String name;
    
      @Lob
      @Column( length = 100000 )
      private String text;
    
      public long getId() {
        return id;
      }
    
      public void setName( String name ) {
        this.name = name;
      }
    
      public String getName() {
        return name;
      }
    
      public String getText() {
        return text;
      }
    
      public void setText( String text ) {
        this.text = text;
      }
    
    }
    

    而我的持久化xml是这样的

    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
                 version="2.0">
       <persistence-unit name="jpatest" transaction-type="RESOURCE_LOCAL">
            <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
                <property name="hibernate.hbm2ddl.auto" value="update"/>
                <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
                <property name="hibernate.connection.username" value="root"/>
                <property name="hibernate.connection.password" value="root"/>
                <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/jpadatabase"/>
                <property name="hibernate.show-sql" value="true"/>
            </properties>
        </persistence-unit>
    </persistence>
    

    如果我更改 hibernate.hbm2ddl.auto 的值来创建实体的文本属性并将其更改为此

    .....
    
      @Column( length = 255 )
      private String text;
    ......
    

    模式生成器在启动时生成以下 sql

    DEBUG SchemaExport:415 - drop table if exists usr
    DEBUG SchemaExport:415 - create table usr (id bigint not null auto_increment, name varchar(40) unique, text varchar(255), primary key (id)) ENGINE=InnoDB
    INFO SchemaExport:281 - schema export complete
    

    现在再次更改实体中的属性

    .....
    @Lob
    @Column( length = 100000 )
    private String text;
    .......
    

    现在生成以下正确的sql

    DEBUG SchemaExport:415 - drop table if exists usr
    DEBUG SchemaExport:415 - create table usr (id bigint not null auto_increment, name varchar(40) unique, text longtext, primary key (id)) ENGINE=InnoDB
    INFO SchemaExport:281 - schema export complete
    

    到目前为止一切顺利。

    现在,如果我将值 hibernate.hbm2ddl.auto 更改为更新并以相同的顺序重复实体中的上述更改,则不会生成更新列 sql,尽管我已经从 varchar(255) 更新了文本列转长文

     INFO TableMetadata:65 - table found: jpadatabase.usr
     INFO TableMetadata:66 - columns: [id, text, name]
     INFO TableMetadata:68 - foreign keys: []
     INFO TableMetadata:69 - indexes: [name, primary]
     DEBUG DefaultIdentifierGeneratorFactory:90 - Setting dialect  [org.hibernate.dialect.MySQL5InnoDBDialect]
     INFO SchemaUpdate:217 - schema update complete
    

    但是,如果我使用更新和而不是修改属性,我添加另一个属性位置,然后再次生成正确的 sql

    DEBUG SchemaUpdate:203 - alter table usr add column location varchar(255)
    INFO SchemaUpdate:217 - schema update complete
    

    所以本质上创建(首先删除表然后重新创建)可以正常工作,但是如果属性元数据有修改,则更新不会。

    在我看来,增量更新的驱动程序支持问题似乎在这里发挥了作用。 同样直观地如果我想到这一点,那么支持更新列的数据类型是没有意义的。如果修改后的列数据类型是早期数据类型的缩小版本,现有数据会发生什么情况。

    【讨论】:

    • 谢谢,这是一个很好的答案,但是....即使我“手动”更改了我的专栏,我仍然得到错误!数据类型确实发生了变化,但 Hibernate 继续像旧数据类型一样处理它。
    • 当我手动更改列时,我在持久化大文本方面没有任何问题。我唯一一次得到异常是当我超过 mysql 可以处理的数据包大小时。例如,当我的数据包大小为 2500103 时出现此异常 - “com.mysql.jdbc.PacketTooBigException: 查询数据包太大 (2500103 > 1048576)。您可以通过设置 max_allowed_pa​​cket' 变量在服务器上更改此值。”我可以成功地保留长度为 1000018 个字符的文本。我没有在上面进行测试。所以你的问题似乎有所不同。
    • 我猜当您使用 JBossAS7 应用程序服务器时,当您在外部更改列数据类型时,连接池持有的连接可能在表元数据方面是陈旧的。尝试重新启动您的服务器,看看这是否是问题所在?
    • 尝试将 Hibernate 日志级别设置为 DEBUG 或 TRACE 并跟踪 hibernate(作为 JPA 提供者)试图在后台执行的操作!
    • 它确实更新了架构。现在我有 longtext,但它一直告诉我数据太长
    【解决方案2】:

    我刚遇到这个问题,看完这个我找到了答案,仔细想想很合乎逻辑,和envers的审计表有关。

    如何重现

    • 您正在使用休眠环境
    • 您在添加注释@Lob 的代码中更改列的类型。

    原因

    Hibernate 只更新原始表,而不是审核过的表,这是 hibernate 所做的:

    ALTER TABLE piece_aud MODIFY notes LONGTEXT;
    

    症状

    MysqlDataTruncation 异常引发了臭名昭著的“Data too long for column 'x'”。

    解决方案

    手动更新审计表的类型。 示例:

    ALTER TABLE piece_aud MODIFY notes LONGTEXT;
    

    或者,您也可以像这样更新列定义(如果您不介意删除重新创建架构):

    @Column(name="notes",columnDefinition="LONGTEXT")
    private String notes;
    

    这非常棘手,因为异常只说列的名称而不是表的名称!!,这就是为什么删除和重新创建架构也有效,因为重新生成审计表。

    【讨论】:

    • 完全忘记了审计表!你拯救了这一天
    • 非常感谢@ZooMMX。我忘记了审计表。
    • 非常感谢您发布此信息 - 这个问题让我发疯了。这确实应该被标记为正确答案。
    【解决方案3】:

    我也有同样的情况,下面的一个对我来说工作顺利

    @Column(name="your_column_name",columnDefinition="LONGTEXT")
    private String notes;
    

    我知道很久以前就有人问过它,但它仍然可能对某人有所帮助。 :)

    【讨论】:

      【解决方案4】:

      我有同样的问题 INSTER 语句直接在数据库上工作,但通过 Hibernate 没有工作并产生 Data truncation: Data too long for column。当 hbm2ddl 更改为 create-drop 时,一切正常。

      但我真正的问题是由于我使用了使用 Hibernate 内置审计功能的审计表。主表已正确更新为增加的大小,但审计表未正确更新,因此写入审计表的所有更改都会导致此数据截断错误。只需手动将 Audit 表中的列更新为正确的大小,一切正常。

      【讨论】:

      • 审计表的部分也是我的问题。感谢您的回答。
      • 审核是我需要的提示 ;) 到目前为止谢谢!
      【解决方案5】:

      我认为这可以解决问题 @Column(columnDefinition="LONGVARCHAR")

      【讨论】:

      • 如果您使用的是 H2/Hibernate 数据库,这完全可以解决问题。
      【解决方案6】:

      没什么!我最终得到: - 数据库备份 - hbm2ddl => 创建-删除 - hbm2ddl => 更新 - 数据库恢复

      疯了! :(

      【讨论】:

        【解决方案7】:
        @Column( length = 100000 )
        private String text;
        

        有效,但您必须DELETE 表格!因为在这种情况下休眠不会更新表。所以你必须强制休眠来重新创建表并且它可以工作!

        【讨论】:

          【解决方案8】:

          对于我来说,我排除了以下属性并且问题消失了

          <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
          

          【讨论】:

            【解决方案9】:

            不过,这是一个很烦人的问题。无需备份整个数据库并更改hibernate.hbm2ddl.auto,而是可以使用面向方言的ALTER TABLE SQL。例如;对于 MySQL,只需使用

            更新列类型
            alter table your_table modify column your_column text
            

            瞧!

            PS:不要忘记更新审计表!

            【讨论】:

              【解决方案10】:

              在 Spring/JPA/Hibernate/Postgres 中,

              @Type(type="org.hibernate.type.StringClobType")
              String message;
              

              对我来说就像一个魅力!

              重要提示:数据库中的列类型不会自动更新,因此我需要允许 Hibernate 重新创建表,或者手动将类型从 varchar(255) 更改为text,否则似乎不会发生任何事情。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2014-06-24
                • 2021-05-09
                • 2021-11-06
                • 1970-01-01
                • 2023-02-08
                • 2016-03-16
                相关资源
                最近更新 更多