0

类似但不完全的问题。在这些情况下(以及在 JPAContainer 示例中),ManyToOne 关系的 Entity 部分只有一个键。在我的情况下,它是一个嵌入式 id。

我的代码基于通讯簿示例

以下是三个实体:

@Entity
@Table(name = "tutorial")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Tutorial.findAll", query = "SELECT t FROM Tutorial t"),
    @NamedQuery(name = "Tutorial.findById", query = "SELECT t FROM Tutorial t WHERE t.tutorialPK.id = :id"),
    @NamedQuery(name = "Tutorial.findByTutorialTypeId", query = "SELECT t FROM Tutorial t WHERE t.tutorialPK.tutorialTypeId = :tutorialTypeId"),
    @NamedQuery(name = "Tutorial.findByMessage", query = "SELECT t FROM Tutorial t WHERE t.message = :message")})
public class Tutorial implements Serializable {

    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected TutorialPK tutorialPK;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 245)
    @Column(name = "message")
    private String message;
    @JoinColumn(name = "tutorial_type_id", referencedColumnName = "id", insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private TutorialType tutorialType;

    public Tutorial() {
    }

    public Tutorial(TutorialPK tutorialPK) {
        this.tutorialPK = tutorialPK;
    }

    public Tutorial(TutorialPK tutorialPK, String message) {
        this.tutorialPK = tutorialPK;
        this.message = message;
    }

    public Tutorial(int id, int tutorialTypeId) {
        this.tutorialPK = new TutorialPK(id, tutorialTypeId);
    }

    public TutorialPK getTutorialPK() {
        return tutorialPK;
    }

    public void setTutorialPK(TutorialPK tutorialPK) {
        this.tutorialPK = tutorialPK;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public TutorialType getTutorialType() {
        return tutorialType;
    }

    public void setTutorialType(TutorialType tutorialType) {
        this.tutorialType = tutorialType;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (tutorialPK != null ? tutorialPK.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Tutorial)) {
            return false;
        }
        Tutorial other = (Tutorial) object;
        if ((this.tutorialPK == null && other.tutorialPK != null) || (this.tutorialPK != null && !this.tutorialPK.equals(other.tutorialPK))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "games.jwrestling.server.game.db.persistence.Tutorial[ tutorialPK=" + tutorialPK + " ]";
    }

}

和 EmbeddedId 类:

@Embeddable
public class TutorialPK implements Serializable {

    @Basic(optional = false)
    @NotNull
    @Column(name = "id")
    private int id;
    @Basic(optional = false)
    @NotNull
    @Column(name = "tutorial_type_id")
    private int tutorialTypeId;

    public TutorialPK() {
    }

    public TutorialPK(int id, int tutorialTypeId) {
        this.id = id;
        this.tutorialTypeId = tutorialTypeId;
    }

    public int getId() {
        return id;
    }

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

    public int getTutorialTypeId() {
        return tutorialTypeId;
    }

    public void setTutorialTypeId(int tutorialTypeId) {
        this.tutorialTypeId = tutorialTypeId;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (int) id;
        hash += (int) tutorialTypeId;
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof TutorialPK)) {
            return false;
        }
        TutorialPK other = (TutorialPK) object;
        if (this.id != other.id) {
            return false;
        }
        if (this.tutorialTypeId != other.tutorialTypeId) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "games.jwrestling.server.game.db.persistence.TutorialPK[ id=" + id + ", tutorialTypeId=" + tutorialTypeId + " ]";
    }

}

还有一个:

@Entity
@Table(name = "tutorial_type")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "TutorialType.findAll", query = "SELECT t FROM TutorialType t"),
    @NamedQuery(name = "TutorialType.findById", query = "SELECT t FROM TutorialType t WHERE t.id = :id"),
    @NamedQuery(name = "TutorialType.findByType", query = "SELECT t FROM TutorialType t WHERE t.type = :type"),
    @NamedQuery(name = "TutorialType.findByDescription", query = "SELECT t FROM TutorialType t WHERE t.description = :description")})
public class TutorialType implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "TutorialTypeGen")
    @TableGenerator(name = "TutorialTypeGen", table = "jwrestling_id",
            pkColumnName = "tablename",
            valueColumnName = "last_id",
            pkColumnValue = "tutorial_type",
            allocationSize = 1,
            initialValue = 1)
    @Column(name = "id")
    private Integer id;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 45)
    @Column(name = "type")
    private String type;
    @Size(max = 245)
    @Column(name = "description")
    private String description;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "tutorialType")
    private List<Tutorial> tutorialList;

    public TutorialType() {
    }

    public TutorialType(Integer id) {
        this.id = id;
    }

    public TutorialType(Integer id, String type) {
        this.id = id;
        this.type = type;
    }

    public TutorialType(String type, String desc) {
        this.type = type;
        this.description = desc;
    }

    public Integer getId() {
        return id;
    }

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

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getDescription() {
        return description;
    }

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

    @XmlTransient
    public List<Tutorial> getTutorialList() {
        return tutorialList;
    }

    public void setTutorialList(List<Tutorial> tutorialList) {
        this.tutorialList = tutorialList;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof TutorialType)) {
            return false;
        }
        TutorialType other = (TutorialType) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "games.jwrestling.server.game.db.persistence.TutorialType[ id=" + id + " ]";
    }

}

我设置了表单,当我编辑项目时它工作正常,但创建时我得到错误,因为 TutorialPK 为空:

Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.2.v20151217-774c696): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'tutorial_type_id' cannot be null

以下是相关的 Vaadin 代码:

public final class TutorialEditor extends Window implements Button.ClickListener,
        FormFieldFactory {

    private final Item tutorialItem;
    private final Form editorForm;
    private final Button saveButton;
    private final Button cancelButton;

    public TutorialEditor(Item tutorialItem) {
        this.tutorialItem = tutorialItem;
        editorForm = new Form();
        editorForm.setFormFieldFactory(this);
        editorForm.setBuffered(true);
        editorForm.setImmediate(true);
        editorForm.setItemDataSource(tutorialItem, Arrays.asList("message",
                "tutorialType"));

        saveButton = new Button("Save", this);
        cancelButton = new Button("Cancel", this);

        editorForm.getFooter().addComponent(saveButton);
        editorForm.getFooter().addComponent(cancelButton);
        setSizeUndefined();
        setContent(editorForm);
        setCaption("New Tutorial");
    }

    @Override
    public void buttonClick(Button.ClickEvent event) {
        if (event.getButton() == saveButton) {
            editorForm.commit();
            fireEvent(new EditorSavedEvent(this, tutorialItem));
        } else if (event.getButton() == cancelButton) {
            editorForm.discard();
        }
        close();
    }

    @Override
    public Field<?> createField(Item item, Object propertyId, Component uiContext) {
        Field field = DefaultFieldFactory.get().createField(item, propertyId,
                uiContext);
        if ("tutorialType".equals(propertyId)) {
            field = new TutorialTypeSelector();
        } else if (field instanceof TextField) {
            ((TextField) field).setNullRepresentation("");
        }

        field.addValidator(new BeanValidator(Tutorial.class, propertyId
                .toString()));

        return field;
    }

    public void addListener(EditorSavedListener listener) {
        try {
            Method method = EditorSavedListener.class.getDeclaredMethod(
                    "editorSaved", new Class[]{EditorSavedEvent.class});
            addListener(EditorSavedEvent.class, listener, method);
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error, editor saved method not found");
        }
    }

    public void removeListener(EditorSavedListener listener) {
        removeListener(EditorSavedEvent.class, listener);
    }

    public static class EditorSavedEvent extends Component.Event {

        private final Item savedItem;

        public EditorSavedEvent(Component source, Item savedItem) {
            super(source);
            this.savedItem = savedItem;
        }

        public Item getSavedItem() {
            return savedItem;
        }
    }

    public interface EditorSavedListener extends Serializable {

        public void editorSaved(EditorSavedEvent event);
    }

还有一个:

class TutorialTypeSelector extends CustomField<TutorialType> {

    private final JPAContainer<TutorialType> container;
    private final ComboBox type = new ComboBox();

    public TutorialTypeSelector() {
        container = JPAContainerFactory.make(TutorialType.class,
                "JWPUJNDI");
        setCaption("Type");
        type.setContainerDataSource(container);
        type.setItemCaptionPropertyId("type");
        type.addListener(new Property.ValueChangeListener() {
            @Override
            public void valueChange(
                    com.vaadin.data.Property.ValueChangeEvent event) {
                /*
                 * Modify the actual value of the custom field.
                 */
                if (type.getValue() == null) {
                    setValue(null, false);
                } else {
                    TutorialType entity = container
                            .getItem(type.getValue()).getEntity();
                    setValue(entity, false);
                }
            }
        });
    }

    @Override
    protected Component initContent() {
        CssLayout cssLayout = new CssLayout();
        cssLayout.addComponent(type);
        return cssLayout;
    }

    @Override
    public void setPropertyDataSource(Property newDataSource) {
        super.setPropertyDataSource(newDataSource);
        setTutorialType((TutorialType) newDataSource.getValue());
    }

    @Override
    public void setValue(TutorialType newValue) throws ReadOnlyException,
            Converter.ConversionException {
        super.setValue(newValue);
        setTutorialType(newValue);
    }

    private void setTutorialType(TutorialType type) {
        this.type.setValue(type != null ? type.getId() : null);
    }

    @Override
    public Class<? extends TutorialType> getType() {
        return TutorialType.class;
    }
}

关于如何填充此字段的任何想法?

更新:

使用@MapsId 后出错

Exception [EclipseLink-46] (Eclipse Persistence Services - 2.6.2.v20151217-774c696): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: There should be one non-read-only mapping defined for the primary key field [tutorial.tutorial_type_id].
Descriptor: RelationalDescriptor(games.jwrestling.server.game.db.persistence.Tutorial --> [DatabaseTable(tutorial)])
4

1 回答 1

1

两种方式。如果使用 JPA 1.0,您将需要从引用的 tutorialType 中提取值并将其手动添加到 tutorial.tutorialPK.tutorialTypeId。您没有包含 tutorialType 实体,但如果生成了它的 ID 值,您可能需要在分配值之前将其持久化并刷新。

如果使用 JPA 2.0,您可以在实体中指定@MapsId注释,从而允许 JPA 为您从 tutorial.tutorialType 参考中设置 tuturial.tutorialPK.tutorialTypeId 值:

public class Tutorial implements Serializable {

    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected TutorialPK tutorialPK;
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 245)
    @Column(name = "message")
    private String message;
    @MapsId("tutorialTypeId")
    @ManyToOne(optional = false)
    private TutorialType tutorialType;

但是,一旦创建教程,您将无法更改与教程关联的 TutorialType - 唯一的选择是删除现有的并使用新值创建一个新的。

于 2016-04-06T19:47:01.373 回答