3

在近期的项目中我尝试着把本应用@ManyToOne注解的地方使用@OneToOne注解,本以为进行存储或者查询等操作时会发生错误但是并没有发生任何错误,所有操作都可以正常执行。
所以首先我想看一下在数据库中生成的外键是否相同。
图片.png

  @ApiModelProperty("车辆品牌")
  @ManyToOne
  private VehicleBrand brand;

  @ApiModelProperty("车辆类型")
  @OneToOne
  private VehicleType type;

我们会发现目前在数据库中没有任何差别,那么这时我就想通过查看他们的源代码来比较一下。

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ManyToOne {
  Class targetEntity() default void.class;

  CascadeType[] cascade() default {};

  FetchType fetch() default FetchType.EAGER;

  boolean optional() default true;
}
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OneToOne {
  Class targetEntity() default void.class;

  CascadeType[] cascade() default {};

  FetchType fetch() default FetchType.EAGER;

  boolean optional() default true;

  String mappedBy() default "";

  boolean orphanRemoval() default false;
}

通过对比我们可以发现@OneToOne只比@ManyToOne多了两项内容,mappedBy和orphanRemoval。其余部分完全一致。

MappedBy

只有OneToOne,OneToMany,ManyToMany上才有mappedBy属性,ManyToOne不存在该属性;
mappedBy标签一定是定义在被拥有方的,他指向拥有方;

mappedBy表示声明自己不是一对多的关系维护端,由对方来维护,是在一的一方进行声明的。mappedBy的值应该为一的一方的表名。

mappedBy在Stack Overflow上有着更详细的解释
询问者问题:mappedBy作用

Foo实体:

@ManyToMany(mappedBy = "foos")
private Set<Bar> bars
Bar实体:

@ManyToMany
private Set<Foo> foos

将其中的一个回答翻译过来就是:

如果关联是双向的,则一方必须是所有者,另一方必须是反向端(即在更新关联表中的关系值时将忽略它):
所以,具有mappedby属性的边是相反的边。没有mappedby属性的一方是所有者。(mappedy在Foo中)
所有者侧是Hibernate所查看的哪一个关联存在的方。因此,例如,如果在Bar的foo集合中添加foo,
hibernate将在表中插入一个新行。相反,如果在Foo的bar集合中添加一个bar,则数据库中不会修改任何内容。

尝试着对以上内容进行论证
假设Klass和Teacher两个实体,他们的关系为多对多;

@Entity
public class Teacher  {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    
    @ManyToMany(mappedBy = "teacherList")
    private List<Klass> klassList = new ArrayList<>();
    
}
@Entity
public class Teacher  {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    //teacher拥有mappedBy属性的Klass
    @ManyToMany(mappedBy = "teacherList")
    private List<Klass> klassList = new ArrayList<>();
    
}

这时从klas插入teacher是可以成功的,而从teacher插入klass是没法成功的。

所以我尝试着在VehicleType实体中声明vehicle。

  @OneToOne(mappedBy = "type")
  private Vehicle vehicle;

之后我再打开项目中相应页面时就会发生报错:
图片.png
其中的 More than one row with the given identifier was found:翻译过来就是:找到了多个具有给定标识符的行。也就符合我们的预期。由于之前对mappedBy的认知不清晰,也就没有要给相关联实体增添mappedBy的习惯。

由此可以得知mappedBy不仅仅可以做到上面那样的规定维护方的功能,还可以做到检测存储错误的功能,所以在实际的编写中添加对应的mappedBy还是很有必要的。

至于注解中的其他属性则是对应着jpa级联(Cascade)操作

由于 重复性的操作十分烦琐,尤其是在处理多个彼此关联对象情况下,此时我们可以使用级联(Cascade)操作。级联 在关联映射中是个重要的概念,指当主动方对象执行操作时,被关联对象(被动方)是否同步执行同一操作。
由于暂时并未遇到相应的问题也就没有深入了解,可以先对此有个概念,之后遇到相应问题时再来仔细了解。
参考文章:jpa级联(Cascade)操作

小问题:在新建后台实体时要保证与前台实体的各字段名称保持一致,否则就会出现返回的信息与前台不匹配的情况。
比如前台设置的颜色字段为color,后台为colour那么前台就接收不到后台返还的color字段。


李明
441 声望18 粉丝