有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java用JPA2.0注释m:n与助手类(2个外键+附加属性)的关系

我是JPA 2.0新手,在注释使用外键类的n:m关系时遇到困难,该外键类具有描述该关系的附加属性:

客户可以订阅多个杂志,每个Subscription都是为一个Customer和一个Magazine创建的,另外还节省了订阅时间

这是我的注释类,我使用字段访问。我省略了一些样板代码,比如构造函数、setter、getter(未注释)和方法toStringequalshashCode

@Entity
public class Customer {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @ManyToMany
    private Set<Subscription> subscriptions;

    // ..
 }

@Entity
public class Magazine {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToMany
    private Set<Subscription> subscriptions;

    // ..
}

@Entity
public class Subscription {
    private Date start;
    private Date end;

    @EmbeddedId
    private SubscriptionId id;

    // ..
}

@Embeddable
public class SubscriptionId implements Serializable {
    @ManyToOne
    private Customer customer;
    @ManyToOne
    private Magazine magazine;

    // ..
}

我通过创建和持久化几个对象来测试注释,如下所示:

public static void main(String[] args) {
    EntityManagerFactory emf = Persistence
            .createEntityManagerFactory("kiosk");
    EntityManager em = emf.createEntityManager();

    persist(em);

    em.close();
    emf.close();
}

private static void persist(EntityManager em) {
    em.getTransaction().begin();

    Magazine mag1 = new Magazine("mag1");
    Magazine mag2 = new Magazine("mag2");

    Customer cus1 = new Customer("cus1");
    Customer cus2 = new Customer("cus2");
    Customer cus3 = new Customer("cus3");

    Subscription sub1 = new Subscription(cus1, mag1);
    Subscription sub2 = new Subscription(cus2, mag1);
    Subscription sub3 = new Subscription(cus2, mag2);
    Subscription sub4 = new Subscription(cus3, mag2);

    em.persist(mag1);
    em.persist(mag2);

    em.persist(cus1);
    em.persist(cus2);
    em.persist(cus3);

    em.persist(sub1);
    em.persist(sub2);
    em.persist(sub3);
    em.persist(sub4);

    em.getTransaction().commit();
}

提供程序创建以下MySQL数据库:

mysql> show tables;
+-----------------------+
| Tables_in_kiosk       |
+-----------------------+
| customer              |
| customer_subscription |
| magazine              |
| magazine_subscription |
| subscription          |
+-----------------------+

只有三个表customermagazinesubscription有内容:

mysql> select * from customer;
+-------------+------+
| customer_id | name |
+-------------+------+
|           1 | cus1 |
|           2 | cus2 |
|           3 | cus3 |
+-------------+------+

mysql> select * from magazine;
+-------------+------+
| magazine_id | name |
+-------------+------+
|           1 | mag1 |
|           2 | mag2 |
+-------------+------+

mysql> select * from subscription;
+------+-------+-------------+-------------+
| end  | start | magazine_id | customer_id |
+------+-------+-------------+-------------+
| NULL | NULL  |           1 |           1 |
| NULL | NULL  |           1 |           2 |
| NULL | NULL  |           2 |           2 |
| NULL | NULL  |           2 |           3 |
+------+-------+-------------+-------------+

如果我知道订阅的密钥,我就可以阅读订阅。不过,我还没有试着为客户或杂志阅读整本书

private static void find(EntityManager em) {
    Magazine mag1 = em.find(Magazine.class, 1L);
    Magazine mag2 = em.find(Magazine.class, 2L);

    Customer cus1 = em.find(Customer.class, 1L);
    Customer cus2 = em.find(Customer.class, 2L);
    Customer cus3 = em.find(Customer.class, 3L);

    Subscription sub1 = em.find(Subscription.class, new SubscriptionId(cus1, mag1));
    Subscription sub2 = em.find(Subscription.class, new SubscriptionId(cus2, mag1));
    Subscription sub3 = em.find(Subscription.class, new SubscriptionId(cus2, mag2));
    Subscription sub4 = em.find(Subscription.class, new SubscriptionId(cus3, mag2));

    System.out.println(mag1);
    System.out.println(mag2);

    System.out.println(cus1);
    System.out.println(cus2);
    System.out.println(cus3);

    System.out.println(sub1);
    System.out.println(sub2);
    System.out.println(sub3);
    System.out.println(sub4);
}

印刷品:

Magazine [id=1, name=mag1, subscriptions=null]
Magazine [id=2, name=mag2, subscriptions=null]
Customer [id=1, name=cus1, subscriptions=null]
Customer [id=2, name=cus2, subscriptions=null]
Customer [id=3, name=cus3, subscriptions=null]
Subscription [start=null, end=null, id=SubscriptionId [customer=1, magazine=1]]
Subscription [start=null, end=null, id=SubscriptionId [customer=2, magazine=1]]
Subscription [start=null, end=null, id=SubscriptionId [customer=2, magazine=2]]
Subscription [start=null, end=null, id=SubscriptionId [customer=3, magazine=2]]

两个表customer_subscriptionmagazine_subscription仍然是空的。但在我看来,它们甚至都不是必需的——其他三张桌子看起来和我想要的一模一样。所以我的问题是:

如何在不创建多余表格的情况下,正确地将本例中使用的m:n关系建模为JPA 2.0,同时保留为杂志或客户写入和读取所有订阅的能力

如果有人对代码感兴趣,我已经上传到这里:http://goo.gl/qSc2e;您需要一个名为“kiosk”的MySQL 5数据库,在本地主机上的3306端口运行,根密码为空


共 (2) 个答案

  1. # 1 楼答案

    你的模型实际上没有任何多对多的关系

    CustomerSubscription是一对多。 MagazineSubscription是一对多

    尝试将此作为实体模型:

    @Entity
    public class Customer {
    
        @Id
        @GeneratedValue
        private Long id;
    
        private String name;
    
        @OneToMany
        private Set<Subscription> subscriptions;
    
        // ..
     }
    
    @Entity
    public class Magazine {
        @Id
        @GeneratedValue
        private Long id;
    
        private String name;
    
        @OneToMany
        private Set<Subscription> subscriptions;
    
        // ..
    }
    
    @Entity
    public class Subscription {
    
        private Date start;
    
        private Date end;
    
        @EmbeddedId
        private SubscriptionId id;
    
        // ..
    }
    
    @Embeddable
    public class SubscriptionId implements Serializable {
    
        @ManyToOne
        private Customer customer;
    
        @ManyToOne
        private Magazine magazine;
    
        // ..
    }
    

    Hibernate不会生成冗余的链接表

  2. # 2 楼答案

    MilkPlusVelocetless的答案是解决方案的一部分,在@OneToMany注释中添加mappedBy属性可以解决以下问题:

    @Entity
    public class Magazine {
        @Id
        @GeneratedValue
        private Long id;
    
        @NotNull
        private String name;
    
        @OneToMany(mappedBy = "id.magazine")
        private Set<Subscription> subscriptions;
    
        // ..
    
    }
    
    @Entity
    public class Customer {
        @Id
        @GeneratedValue
        private Long id;
    
        @NotNull
        private String name;
    
        @OneToMany(mappedBy = "id.customer")
        private Set<Subscription> subscriptions;
    
        // ..
    }
    
    @Entity
    public class Subscription {
        private Date start;
        private Date end;
    
        @EmbeddedId
        private SubscriptionId id;
    
        // ..
    }
    
    @Embeddable
    public class SubscriptionId implements Serializable {
        @ManyToOne
        private Customer customer;
        @ManyToOne
        private Magazine magazine;
    
        // ..
    }
    

    只创建3个表(根据需要),而不是5个表