首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Dropwizard/Hibernate:具有双向关系的持久化对象--许多关系导致错误“不允许列为NULL”。

Dropwizard/Hibernate:具有双向关系的持久化对象--许多关系导致错误“不允许列为NULL”。
EN

Stack Overflow用户
提问于 2015-10-18 13:38:11
回答 1查看 1.2K关注 0票数 1

我正在使用带有Hibernate持久性的Dropwizard。这让我头疼,因为Hibernate在生成SQL以持久化对象之前似乎正在“丢失”对象生成的ID。

我有三个类:ChecklistCheckpointChecklistItem,我把它们建模为双向一对多的关系。

我已经构建了域对象、DAO、公开它的资源以及一系列测试(DAO测试和一个全面集成测试)。

我注意到一些奇怪的(坏的)行为:

  1. 我可以在DAO单元测试中的事务中来回Checklist对象(和子对象)。
  2. 但是,通过Dropwizard REST资源执行往返操作失败。

下面是我认为应该在日志中看到的内容(摘自good DAO测试):

代码语言:javascript
运行
复制
[main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
[main] DEBUG org.hibernate.SQL - insert into Checklists (id, description, locked, name, ownerUserId) values (null, ?, ?, ?, ?)
[main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 1
[main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
[main] DEBUG org.hibernate.SQL - insert into Checkpoints (id, name, checklistId) values (null, ?, ?)
[main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 1
[main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
[main] DEBUG org.hibernate.SQL - insert into ChecklistItems (id, description, name, checkpointId, state, url) values (null, ?, ?, ?, ?, ?)
[main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 1
[main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
[main] DEBUG org.hibernate.SQL - insert into ChecklistItems (id, description, name, checkpointId, state, url) values (null, ?, ?, ?, ?, ?)
[main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 2
[main] DEBUG org.hibernate.engine.spi.ActionQueue - Executing identity-insert immediately
[main] DEBUG org.hibernate.SQL - insert into ChecklistItems (id, description, name, checkpointId, state, url) values (null, ?, ?, ?, ?, ?)
[main] DEBUG o.h.id.IdentifierGeneratorHelper - Natively generated identity: 3
[main] DEBUG o.h.e.i.AbstractFlushingEventListener - Processing flush-time cascades

下面是我在运行集成测试时得到的():

代码语言:javascript
运行
复制
127.0.0.1 - - "PUT /api/cl/v0.1/users HTTP/1.1" 201 - "-" "checklists-server (integration test client)" 240
DEBUG com.misys.uk.checklists.resources.ChecklistResource: Invoked ChecklistResource#createChecklist(uriInfo, newChecklist)
DEBUG org.hibernate.SQL: /* insert com.misys.uk.checklists.core.Checklist */ insert into Checklists (id, description, locked, name, ownerUserId) values (null, ?, ?, ?, ?)
DEBUG org.hibernate.SQL: /* insert com.misys.uk.checklists.core.Checkpoint */ insert into Checkpoints (id, name, checklistId) values (null, ?, ?)
WARN  org.hibernate.engine.jdbc.spi.SqlExceptionHelper: SQL Error: 23502, SQLState: 23502
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper: NULL not allowed for column "CHECKLISTID"; SQL statement:
/* insert com.misys.uk.checklists.core.Checkpoint */ insert into Checkpoints (id, name, checklistId) values (null, ?, ?) [23502-189]
DEBUG org.hibernate.SQL: /* insert com.misys.uk.checklists.core.ChecklistItem */ insert into ChecklistItems (id, description, name, checkpointId, state, url) values (null, ?, ?, ?, ?, ?)
WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper: SQL Error: 23502, SQLState: 23502
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper: NULL not allowed for column "CHECKPOINTID"; SQL statement:
/* insert com.misys.uk.checklists.core.ChecklistItem */ insert into ChecklistItems (id, description, name, checkpointId, state, url) values (null, ?, ?, ?, ?, ?) [23502-189]
ERROR io.dropwizard.jersey.errors.LoggingExceptionMapper: Error handling a request: 319c21e9a8198202
! org.h2.jdbc.JdbcSQLException: NULL not allowed for column "CHECKPOINTID"; SQL statement:
! /* insert com.misys.uk.checklists.core.ChecklistItem */ insert into ChecklistItems (id, description, name, checkpointId, state, url) values (null, ?, ?, ?, ?, ?) [23502-189]
! at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.189.jar:1.4.189]

以下是我的域对象:

代码语言:javascript
运行
复制
@Entity
@Table(name = "Checklists")
public class Checklist {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true)
    private long id = Constants.UNASSIGNED;

    @Column(nullable = false)
    private long ownerUserId;

    @NotEmpty @Length(max = 64) @Column(nullable = false, length = 64)
    private String name;

    @Length(max = 2000) @Column(length = 2000)
    private String description;

    @Column(nullable = false)
    private boolean locked;

    @OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}) @OrderColumn(name = "cpOrderWithinCl")
    @JoinTable(name = "ChecklistToCheckpoint",
            joinColumns = @JoinColumn(name = "checklistId"),
            inverseJoinColumns = @JoinColumn(name = "checkpointId"))
    private List<Checkpoint> checkpoints;

    public Checklist() {
        // needed for Jackson
    }

    @JsonProperty
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @JsonProperty
    public long getOwnerUserId() {
        return ownerUserId;
    }

    public void setOwnerUserId(final long ownerUserId) {
        this.ownerUserId = ownerUserId;
    }

    @JsonProperty
    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    @JsonProperty @JsonInclude(Include.NON_NULL)
    public String getDescription() {
        return description;
    }

    public void setDescription(final String description) {
        this.description = description;
    }

    @JsonProperty
    public List<Checkpoint> getCheckpoints() {
        return checkpoints;
    }

    public void setCheckpoints(final List<Checkpoint> checkpoints) {
        this.checkpoints = Preconditions.checkNotNull(checkpoints);
    }

    private void addCheckpoint(final Checkpoint newCheckpoint) {
        if (checkpoints == null) {
            checkpoints = new LinkedList<>();
        }

        newCheckpoint.setParent(this);
        checkpoints.add(Preconditions.checkNotNull(newCheckpoint));
    }

    private void removeCheckpoint(final Checkpoint checkpoint) {
        if (checkpoints != null) {
            checkpoints.remove(Preconditions.checkNotNull(checkpoint));
        }
    }

    @JsonProperty
    public boolean isLocked() {
        return locked;
    }

    public void setLocked(final boolean locked) {
        this.locked = locked;
    }

    // equals() and hashCode() ...
}

实体Checkpoint,它与ChecklistItem有一个多个关系。

代码语言:javascript
运行
复制
@Entity
@Table(name = "Checkpoints")
public class Checkpoint {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true)
    private long id = Constants.UNASSIGNED;

    @NotEmpty @Length(max = 64) @Column(nullable = false, length = 64)
    private String name;

    @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "checklistId", nullable = false)
    private Checklist parent;

    @OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL) @OrderColumn(name = "ciOrderWithinCp")
    @JoinTable(name = "CheckpointToChecklistItem",
            joinColumns = @JoinColumn(name = "checkpointId"),
            inverseJoinColumns = @JoinColumn(name = "checklistItemId"))
    private List<ChecklistItem> items;

    public Checkpoint() {
        // Needed for Jackson
    }

    @JsonProperty
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @JsonProperty
    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    @JsonIgnore
    public Checklist getParent() {
        return parent;
    }

    public void setParent(final Checklist parent) {
        this.parent = parent;
    }

    @JsonProperty
    public List<ChecklistItem> getItems() {
        return items;
    }

    public void setItems(final List<ChecklistItem> items) {
        this.items = Preconditions.checkNotNull(items);
    }

    public void addItem(final ChecklistItem newItem) {
        if (items == null) {
            items = new LinkedList<>();
        }

        Preconditions.checkNotNull(newItem).setParent(this);
        items.add(newItem);
    }

    private void removeItem(final ChecklistItem item) {
        if (items != null) {
            items.remove(Preconditions.checkNotNull(item));
        }
    }

    // equals() and hashCode() ...
}

实体ChecklistItem

代码语言:javascript
运行
复制
@Entity
@Table(name = "ChecklistItems")
public class ChecklistItem {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true)
    private long id = Constants.UNASSIGNED;

    @NotEmpty @Length(max = 64) @Column(nullable = false, length = 64)
    private String name;

    @Length(max = 2000) @Column(length = 2000)
    private String description;

    @Length(max = 2000) @Column(length = 2000)
    private String url;

    @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "checkpointId", nullable = false)
    private Checkpoint parent;

    @Column(nullable = false, length=16) @NotNull @StringEnumeration(enumClass = ChecklistItemState.class)
    private String state = ChecklistItemState.UNCHECKED.name();

    public ChecklistItem() {
        // needed for Jackson
    }

    @JsonProperty
    public long getId() {
        return id;
    }

    public void setId(final long id) {
        this.id = id;
    }

    @JsonProperty
    public String getName() {
        return name;
    }

    public void setName(final String name) {
        this.name = name;
    }

    @JsonProperty @JsonInclude(Include.NON_NULL)
    public String getDescription() {
        return description;
    }

    public void setDescription(final String description) {
        this.description = description;
    }

    @JsonProperty @JsonInclude(Include.NON_NULL)
    public String getUrl() {
        return url;
    }

    public void setUrl(final String url) {
        this.url = url;
    }

    @JsonIgnore
    public Checkpoint getParent() {
        return parent;
    }

    public void setParent(final Checkpoint parent) {
        this.parent = parent;
    }

    @JsonProperty
    public ChecklistItemState getState() {
        return ChecklistItemState.valueOf(state);
    }

    // equals() and hashCode() ...
}

我相当肯定这与DBMS无关(我尝试过几个不同的关系,也发生了相同的问题),而且with确定它与在事务下运行没有任何关系(我已经用调试println()来证实这一点)。

我怀疑这与我试图在域对象之间执行一个-多个映射的方式有关。

有什么想法吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-10-18 18:55:47

问题很简单:当通过Hibernate (实体之间存在双向关系)持久化对象图时,这些关系将由Hibernate在两个方向上导航。如果它们不正确,Hibernate将失败。

我一开始没有注意到这一点,因为我正在使用Builder构建单元测试中的对象,而生成器本身也经过了彻底的测试--我以为我已经完成了这个任务。显然不是!

集成测试失败了,因为错误在于JSON注释(或缺少)来处理这种双向关系--而这只是在我的全面测试中才被揭示出来的,因为只有那时它才在执行破碎的JSON反向引用行为!

  • 使用@JsonManagedReference@JsonBackReference正确地注释对象以处理双向引用(子->父和父->子)。
  • 如果在上述字段中存在@JsonIgnore注释,上述内容将无法工作。
  • 你在测试所有这些东西,不是吗.?
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33198564

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档