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两个表的主键。表名称和列名称会自动生成,也可以通过注解进行声明。多对多关系建立时,注解所在的表,是关系的维护方,所以当删除维护方时,会同时删除中间表的相关数据。查询操作,会查到关联的集合对象。