之后添加JPA @OrderColumn会导致集合错误的空索引列

gxwragnw  于 2022-11-14  发布在  其他
关注(0)|答案(3)|浏览(184)

假设,实体A和B之间存在@OneToMany关系。每个A可以有多个B。

@Entity
public class A{
  @Id
  private long id;
  ...
  @OneToMany(mappedBy="ref")
  private List<B> items;
  ...
}

@Entity
public class B{
  @Id
  private long id;

  @ManyToOne
  @JoinColumn(name = "A_ID")
  private A ref;
}

此应用程序已投入生产一段时间,保存到数据库中的B的顺序到目前为止并不相关。
然而,现在需要B的确切顺序。为此,我添加了如下的@OrderColumn。

@OneToMany
@OrderColumn(name="B_ORDER")
private List<B> items;

它工作正常,B表现在具有带顺序值的B_ORDER列。
现在需要更新生产数据库。新列B_ORDER已添加到表B中。但是,对于现有行,此列没有值(空)。当您尝试检索已存在记录的B列表时,我得到以下异常:

org.hibernate.HibernateException: null index column for collection

对于旧记录,可以有一个以任何顺序排列的B列表。@OrderColumn具有属性“nullable”,默认情况下为true。但这没有帮助。
是否有JPA方法来实现这一点,或者我必须自己在db中填充这些空值?
将JPA 2.1与Hibernate 4.3.7结合使用。

2guxujil

2guxujil1#

您遇到的问题是,JPA提供程序(在您的Hibernate中)需要 * order列的值的连续(非稀疏)排序 *。
但是,当您将新列添加到新数据库中时,这些值将为null,除非您定义了默认值0。
您可能认为添加一个默认值就可以了。抱歉,事实并非如此。如果这样做,您的集合将始终显示最多一个项目。
要解决这个问题,必须为每个集合添加一个连续的非稀疏编号。
实际上,您应该更改实体临时值,以便可以实际访问相应的字段:

@Entity  
public class B{  
@Id  
private long id;  

@ManyToOne  
@JoinColumn(name = "A_ID")  
private A ref; 

@Column(name = "B_ORDER")
private Integer order;
}

现在你可以加载所有项了,接下来你需要写一个临时方法,从数据库加载所有项,并对每个集合中的每个项进行编号,从0开始,直到集合的长度减1。
上面的建议只是我觉得方便的一种方法。有很多方法可以解决它。如果你有足够的时间,你甚至可以手动完成。
从这个答案中你应该了解的是集合中的项需要从0开始编号。否则Hibernate不会将这些项加载到你的集合中。你需要满足here描述的约束。

b5lpy0ml

b5lpy0ml2#

我也遇到过类似的问题:当我更新一个已有父项的子项列表时,hib似乎没有在order列中生成值。我通过在子项上显式添加order列来修复它,如下所示(使用您的示例):

@Entity
public class B{  
  @Column(name = "B_ORDER")
  private int order;

  ...
}

如果我做不到这一点,创建A记录(带有B子项)会正常工作,但是通过添加B子项来更新A记录会失败(带有null index column for collection: ...),因为hib不会为B_ORDER列生成值。

afdcj2ne

afdcj2ne3#

我采取的步骤

  • 我手动添加了订单列
  • 生成值,然后更新表
  • 使用生成的值更新表
  • 删除的索引
  • 已创建包含image_order的新索引

我用的是postgres。

ALTER TABLE IF EXISTS test.blogposts_images
    ADD COLUMN images_order integer NOT NULL DEFAULT 0;

DO $$
DECLARE blogpostImage RECORD;
DECLARE currentBlogPostID INTEGER = -1;
DECLARE i INTEGER = -1;
BEGIN FOR blogpostImage IN
    SELECT * FROM test.blogposts_images order by blogpost_id, image_id
    LOOP
  
    if blogpostImage.blogpost_id <> currentBlogPostID then
        currentBlogPostID = blogpostImage.blogpost_id;
        i = 0;
    else
        i = i + 1;
    end if;
    RAISE NOTICE 'blogpostId[%] imageId[%] position[%]', blogpostImage.blogpost_id, blogpostImage.image_Id, i;
    UPDATE test.blogposts_images set images_order = i where blogpost_id = blogpostImage.blogpost_id and image_id = blogpostImage.image_Id;
   END LOOP;
END $$

ALTER TABLE test.blogposts_images ALTER COLUMN images_order DROP DEFAULT;

ALTER TABLE test.blogposts_images DROP CONSTRAINT idx_16386_primary;
ALTER TABLE test.blogposts_images ADD PRIMARY KEY (blogpost_id, image_id, images_order);

相关问题