【问题标题】:JPA simple many-to-many with eclipselinkJPA 简单的多对多与 eclipselink
【发布时间】:2013-01-23 01:05:52
【问题描述】:

我有这个 ER 图:

这被翻译成这些类:

User.java

@Entity
@Table(name = "user")
@NamedQueries({
    @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u")})
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Column(name = "name")
    private String name;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
    private Collection<UserHasNotify> userHasNotifyCollection;

UserHasNotify.java

@Entity
@Table(name = "user_has_notify")
@NamedQueries({
    @NamedQuery(name = "UserHasNotify.findAll", query = "SELECT u FROM UserHasNotify u")})
public class UserHasNotify implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected UserHasNotifyPK userHasNotifyPK;
    @Column(name = "has_read")
    private String hasRead;
    @JoinColumn(name = "notify_id", referencedColumnName = "id", insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private Notify notify;
    @JoinColumn(name = "user_id", referencedColumnName = "id", insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private User user;

UserHasNotifyPK.java

@Embeddable
public class UserHasNotifyPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "user_id")
    private int userId;
    @Basic(optional = false)
    @Column(name = "notify_id")
    private int notifyId;

Notify.java

@Entity
@Table(name = "notify")
@NamedQueries({
    @NamedQuery(name = "Notify.findAll", query = "SELECT n FROM Notify n")})
public class Notify implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private Integer id;
    @Column(name = "message")
    private String message;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "notify")
    private Collection<UserHasNotify> userHasNotifyCollection;

现在,我要添加一个实体 User 和 Notify 并在它们之间创建一个关系。 所以我写了这个sn-p:

        User user = new User();
        user.setName("John");

        Notify notify = new Notify();
        notify.setMessage("Hello World");

        userFacade.create(user);
        notifyFacade.create(notify);

        UserHasNotify uhn = new UserHasNotify();
        uhn.setNotify(notify);
        uhn.setUser(user); 
        uhn.setHasRead("ok");
        uhnFacade.create(uhn);

但我收到此错误:

Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'user_id' cannot be null
Error Code: 1048
Call: INSERT INTO user_has_notify (has_read, user_id, notify_id) VALUES (?, ?, ?)
    bind => [3 parameters bound]
Query: InsertObjectQuery(com.test.entity.UserHasNotify[ userHasNotifyPK=null ])

为什么????????????

【问题讨论】:

  • userFacade.create 是做什么的?

标签: jpa many-to-many eclipselink


【解决方案1】:

错误的原因可能在于远程通信。您使用外观的事实意味着您正在与后端进行远程通信。

这意味着您在uhn 实例上设置的usernotify 实例被发送到远程系统上持久化,而本地实例永远不会收到生成的ID。

要验证和解决这个问题,您可以扩展您的示例:

保存usernotify 后,从您的后端获取它们。这应该返回具有现有 ID 的持久实例。然后你可以使用它们来存储你的uhn 关系。

编辑:我错过了UserHasNotify 是带有嵌入 ID 的有状态关系这一事实。在代码中,您从未设置此 Id,因此提供者会错过它。

对于这种情况,我建议使用 IdClass 而不是嵌入的 Id - 映射更具可读性,因此您可能不会两次映射 UserNotify 关系 - 一次在嵌入PK并再次在实体中;)

这是它的样子:

public class UserHasNotifyPK implements Serializable {
    private Notify notify;
    private User user;
    ...
    }

.

@Entity
@Table(name = "user_has_notify")
@IdClass(UserHasNotifyPK.class)
@NamedQueries({
    @NamedQuery(name = "UserHasNotify.findAll", query = "SELECT u FROM UserHasNotify u")})
public class UserHasNotify implements Serializable {
    
    private static final long serialVersionUID = 1L;
        
    @Column(name = "has_read")
    private String hasRead;
    
    @Id
    @JoinColumn(name = "notify_id", referencedColumnName = "id", insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private Notify notify;
    
    @Id
    @JoinColumn(name = "user_id", referencedColumnName = "id", insertable = false, updatable = false)
    @ManyToOne(optional = false)
    private User user;

然后您可以再次尝试相同的测试。

【讨论】:

  • 好吧,也许我明白了。但是如果我执行 User newUser = userFacade.findAll().get(0);通知 newNotify = notifyFacade.findAll().get(0);获取最新的通知和用户。然后我重新尝试使用 newUser 和 newNotify 对象创建一个 uhn,但我有同样的错误。
  • @Cecco - 我知道我错过了一个重要的细节。我已在答案中添加了更多信息,请参阅编辑。
  • 我已经更改了您的编辑,但现在我发现了这个错误:异常描述:无效的复合主键规范。主键类[com.test.entity.UserHasNotifyPK]和实体bean类[com.test.entity.UserHasNotify]中的主键字段或属性的名称必须对应,并且它们的类型必须相同。此外,请确保您已为 XML 中的相应属性指定 ID 元素和/或在实体类的相应字段或属性上指定 @Id。
  • @Cecco - 请原谅我的误导性建议。我太不专心了(-.-)。 PK 类中的字段必须与实体中的 id 字段类型相同,名称相同。我已经更正了 Id 类。
  • 好的,你是对的,我已经修复了它,但现在我有一个新错误。抱歉,我是 JPA eclipselink 的新手...Exception [EclipseLink-46] (Eclipse Persistence Services - 2.3.2.v20111125-r10461): org.eclipse.persistence.exceptions.DescriptorException 异常描述:应该有一个为主键字段 [user_has_notify.notify_id] 定义的非只读映射。描述符:RelationalDescriptor(com.test.entity.UserHasNotify --> [DatabaseTable(user_has_notify)])
猜你喜欢
  • 2011-05-08
  • 2013-04-13
  • 1970-01-01
  • 2012-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多