前提:A 表和 B 表为oneToMany的关系,B中属性a_id为外键。
单向关系和双向关系:双向为A、B entity类中都依赖对方,循环依赖,双向才需要指定mappedBy。
在@oneToMany、@ManyToMany、@OneToOne能定义mappedBy,@ManyToOne不能。定义了mappedBy的属性,或者说A的entity中不能再使用@JoinColumn,@JoinTable。
双向:
A中注解
@OneToMany(mappedBy = "a")
private List<B> blist;
B中注解
@ManyToOne(targetEntity = A.class)
@JoinColumn(name="a_id", referencedColumnName = "id")
private A a;
单向,@JoinColumn方式:
A或B中注解
@OneToMany
@JoinColumn(name = "a_id", referencedColumnName = "id")
private List<B> blist;
//@ManyToOne
//@JoinColumn(name = "a_id", referencedColumnName = "id")
//private A a;
单向,@JoinTable方式:
A或B中注解
@OneToMany
@JoinTable(name = "A_B",
joinColumns = @JoinColumn(name = "a_id", referencedColumnName ="id"),
inverseJoinColumns = @JoinColumn(name="b_id", referencedColumnName ="id"))
private List<B> blist;
//@ManyToOne
//@JoinTable(name = "A_B",
//joinColumns = @JoinColumn(name = "b_id", referencedColumnName ="id"),
//inverseJoinColumns = @JoinColumn(name="a_id", referencedColumnName ="id"))
//private A a;
N+1 问题: 当A表中有n条数据, 需要查询1次A表,再查询B表N次。 反过来ManyToOne, N也是对于A表的n条数据。
双向:以ARepository查询为例
情况一:A饿加载,无视B的FetchType。
N 对应的查询sql语句是只查B表。
B中声明的A属性指向与自身关联的A对象。 a.equals(a.getBlist().get(0).getA()) == true
情况二:A懒加载,B饿加载。
N 对应的查询sql语句是分开先后查B,A表。B中声明的A属性是一个新对象。 懒加载get不会执行sql,sysout才会。
情况三:A懒加载,B懒加载。
N 对应的查询sql语句是只查B表。 a.getBlist().get(0).getA()获取A时才会查A表,B中声明的A属性是一个新对象。
单向@JoinColumn方式:
懒饿加载一样,N 对应的查询sql语句是只查B表或只查A表。
单向@JoinTable方式:
a_id和b_id属于A_B中间表的字段。 joinColumns是中间表和本entity的mapping,inverseJoinColumns是中间表和另一个entity的mapping。
referencedColumnName 必须是A,B主键。基于此,B中的外键就无用了,B->A_B为一对一,A->A_B为一对多。
@OneToMany,懒饿加载一样,N 对应的查询sql语句是join。
@ManyToOne,懒饿加载一样,1对应的sql语句是join。 N 对应的查询sql语句是A表。