23

我的一个实体Machinery有一个String名为notes. 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列更改为 a LONGTEXT(这对我来说已经足够了)。

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

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

最后,我DROP将我的本地/暂存数据库模式更改为hibernate.hbm2ddl.auto并且它可以工作。persistence.xmlcreate

JPA 是否仍然认为它是一个VARCHAR? 它是否有某种缓存或存储模式信息的某个地方?

显然,我不能放弃我的生产数据库。那么,我可以做些什么来重置或更改列类型?

我正在使用 JBossAS7 JPA 2-Hibernate。

4

10 回答 10

25

权威的Hibernate 书籍“Java persistence with Hibernate”提到了hibernate.hbm2ddl.auto的更新值(粗体是我的)

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

Hibernate docs也在这里提出相同的建议

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 更改为更新并以相同的顺序重复实体中的上述更改,尽管我已将文本列从 varchar(255) 更新为 LONGTEXT,但不会生成更新列 sql

 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

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

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

于 2012-09-13T11:24:46.190 回答
20

我刚遇到这个问题,看完这个我找到了答案,如果你想它是非常合乎逻辑的,与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;

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

于 2015-08-03T01:30:37.133 回答
10

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

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

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

于 2018-08-12T16:10:39.507 回答
4

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

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

于 2013-04-11T09:46:01.447 回答
4

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

于 2014-01-07T08:57:23.333 回答
3

没有!我最终得到: - 数据库备份 - hbm2ddl => CREATE-DROP - hbm2ddl => 更新 - 数据库恢复

疯狂的!:(

于 2012-10-12T13:30:52.900 回答
3
@Column( length = 100000 )
private String text;

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

于 2019-07-05T20:08:06.190 回答
1

至于我,我排除了以下属性,问题就消失了

<property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.ImprovedNamingStrategy"/>
于 2014-10-20T03:59:33.183 回答
0

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

alter table your_table modify column your_column text

瞧!

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

于 2014-11-10T14:04:52.363 回答
0

在 Spring/JPA/Hibernate/Postgres 中,

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

对我来说就像一个魅力!

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

于 2016-07-30T13:53:02.860 回答