欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JPA中OneToMany

程序员文章站 2022-04-22 21:56:02
...

@ManyToMany

我们假设有咖啡,订单。咖啡和订单的关系就是多对多的关系。一个咖啡可以属于多个订单,一个订单也可以包含多种咖啡。

示例涉及的表:

咖啡类:

@Entity
@Table(name = "T_MENU")
@Builder
@Data
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class Coffee extends BaseEntity implements Serializable {
    private String name;
    private Money price;
}

订单类:

@Entity
@Table(name = "T_ORDER")
@Data
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CoffeeOrder extends BaseEntity implements Serializable {
    private String customer;
    @ManyToMany
    @JoinTable(name = "T_ORDER_COFFEE")
    @OrderBy("id")
    private List<Coffee> items;
    @Enumerated
    @Column(nullable = false)
    private OrderState state;
}

订单的items属性上标注@ManyToMany。@JoinTable可以指定生成的中间表表名。

如果不指定,会采用表名+变量名的命名方式生成中间表:t_order_items

注解产生的作用:

查看Hibernate建表的打印语句:

Hibernate: 
    
    create table t_coffee (
       id bigint not null auto_increment,
        create_time datetime,
        update_time datetime,
        age varchar(255),
        name varchar(255),
        price decimal(19,2),
        primary key (id)
    ) engine=MyISAM
Hibernate: 
    
    create table t_order (
       id bigint not null auto_increment,
        create_time datetime,
        update_time datetime,
        customer varchar(255),
        state integer not null,
        primary key (id)
    ) engine=MyISAM
Hibernate: 
    
    create table t_order_coffee (
       coffee_order_id bigint not null,
        items_id bigint not null
    ) engine=MyISAM
   
Hibernate: 
    
    alter table t_order_coffee 
       add constraint FKcm20cgbnblo27okpnlk0njdwy 
       foreign key (items_id) 
       references t_coffee (id)
Hibernate: 
    
    alter table t_order_coffee 
       add constraint FK33ucji9dx64fyog6g17blpx9v 
       foreign key (coffee_order_id) 
       references t_order (id)

可以看出,标注@ManyToMany后,会生成一个中间表t_order_coffee,

它会把咖啡表和订单表的id作为表的两个列。名称的生成规则是,注解所在表的外键 列根据当前类加上id 比如:coffee_order_id , 另一个则根据变量名+id:items_id

随后的alter语句,则将中间表的两个列分别和对应的表做外键关联。

也就是说,中间表每条记录保存了两个外键对应的值。并且订单是维护方。

完整的注解如下:

 @ManyToMany
 @JoinTable(name = "T_ORDER_COFFEE",joinColumns = @JoinColumn(name = "coffee_order_id",referencedColumnName = "id"),
            inverseJoinColumns = @JoinColumn(name="coffee_id",referencedColumnName = "id"))
    @OrderBy("id")
    private List<Coffee> items;

可以指定生成中间表 的表名,列名称以及关联的外键。@OrderBy这里是对取出的集合做一个排序。

另外只需要在咖啡和订单类两者的一个标注注解即可。

如何保存数据和查询数据:

  • 保存数据:
		//创建一个咖啡对象并保存
		Coffee latte = Coffee.builder().name("latte")
				.price(Money.of(CurrencyUnit.of("CNY"), 30.0))
				.build();
		coffeeRepository.save(latte);
		//创建第二个咖啡对象
		Coffee espresso = Coffee.builder().name("espresso")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.build();
		coffeeRepository.save(espresso);
		//创建一个订单,并将一个咖啡集合设置进去,最后保存后,中间表就会产生一条记录
		CoffeeOrder order = CoffeeOrder.builder()
				.customer("Li Lei")
				.items(Collections.singletonList(espresso))
				.state(OrderState.INIT)
				.build();
		orderRepository.save(order);
  • 查询记录:
//查询订单集合,则咋结果集中就会有对应的咖啡集合。
list = orderRepository.findByCustomerOrderById("Li Lei");
  • 删除:

如果此时执行删除订单操作:

orderRepository.deleteById(1L);

实际会执行两条删除语句:会同时删掉订单和中间表中和该订单关联的数据。

但是如果删除得是咖啡,则中间表的数据不会删除。

Hibernate: 
    delete 
    from
        t_order_coffee 
    where
        coffee_order_id=?
Hibernate: 
    delete 
    from
        t_order 
    where
        id=?

总结:

@ManyToMany注解的作用是帮助我们生成一张维护A和B两者关系的中间表。中间表维护了A,B两个表的主键。表名称和列名称会自动生成,也可以通过注解进行声明。多对多关系建立时,注解所在的表,是关系的维护方,所以当删除维护方时,会同时删除中间表的相关数据。查询操作,会查到关联的集合对象。