似たような質問がありますが、完全ではありません。そのような場合 (および JPAContainer の例) では、ManyToOne 関係の Entity 部分に 1 つのキーがあります。私の場合、それは埋め込みIDです。
私のコードはAddress Book exampleに基づいています。
以下に 3 つのエンティティを示します。
@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 が null であるため、作成するとエラーが発生します。
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;
}
}
このフィールドに入力する方法について何か考えはありますか?
アップデート:
@MapId 使用後のエラー
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)])