连接查询与嵌套查询是常见的查询问题,获取一个表的内容时也需要获取其外键所引用表的内容。
在JPA中,我们需要先在实体类中指定这种外键关系,通过@JoinColumn
注解,并确定对应关系来实现:
@Data
@Entity
@Table(name = "users_detail")
public class AccountDetail {
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
int id;
@Column(name = "address")
String address;
@Column(name = "email")
String email;
@Column(name = "phone")
String phone;
@Column(name = "real_name")
String realName;
}
@Data
@Entity
@Table(name = "users")
public class Account {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
@Id
int id;
@Column(name = "username")
String username;
@Column(name = "password")
String password;
@JoinColumn(name = "detail_id") //指定存储外键的字段名称
@OneToOne //声明为一对一关系
AccountDetail detail;
}
设定完毕后启动项目,Hibernate会自动修改表结构:
Hibernate: alter table users add column detail_id integer
Hibernate: create table users_detail (id integer not null auto_increment, address varchar(255), email varchar(255), phone varchar(255), real_name varchar(255), primary key (id)) engine=InnoDB
Hibernate: alter table users add constraint FK7gb021edkxf3mdv5bs75ni6jd foreign key (detail_id) references users_detail (id)
此时再次查询会直接将关联数据一并添加:
@Test
void pageAccount() {
repository.findById(1).ifPresent(System.out::println);
}
Hibernate: select account0_.id as id1_0_0_, account0_.detail_id as detail_i4_0_0_, account0_.password as password2_0_0_, account0_.username as username3_0_0_, accountdet1_.id as id1_1_1_, accountdet1_.address as address2_1_1_, accountdet1_.email as email3_1_1_, accountdet1_.phone as phone4_1_1_, accountdet1_.real_name as real_nam5_1_1_ from users account0_ left outer join users_detail accountdet1_ on account0_.detail_id=accountdet1_.id where account0_.id=?
Account(id=1, username=Test, password=123456, detail=AccountDetail(id=1, address=四川省成都市青羊区, email=8371289@qq.com, phone=1234567890, realName=本伟))
懒加载
那要是我们只想要Account的数据,不想要用户详细信息数据怎么办呢?我希望在我要用的时候再获取详细信息,这样可以节省一些网络开销,我们可以设置懒加载,这样只有在需要时才会向数据库获取:
@JoinColumn(name = "detail_id")
@OneToOne(fetch = FetchType.LAZY) //将获取类型改为LAZY
AccountDetail detail;
接着我们测试一下:
@Transactional //懒加载属性需要在事务环境下获取,因为repository方法调用完后Session会立即关闭
@Test
void pageAccount() {
repository.findById(1).ifPresent(account -> {
System.out.println(account.getUsername()); //获取用户名
System.out.println(account.getDetail()); //获取详细信息(懒加载)
});
}
Hibernate: select account0_.id as id1_0_0_, account0_.detail_id as detail_i4_0_0_, account0_.password as password2_0_0_, account0_.username as username3_0_0_ from users account0_ where account0_.id=?
Test
Hibernate: select accountdet0_.id as id1_1_0_, accountdet0_.address as address2_1_0_, accountdet0_.email as email3_1_0_, accountdet0_.phone as phone4_1_0_, accountdet0_.real_name as real_nam5_1_0_ from users_detail accountdet0_ where accountdet0_.id=?
AccountDetail(id=1, address=四川省成都市青羊区, email=8371289@qq.com, phone=1234567890, realName=卢本)
可以看到,获取用户名之前,并没有去查询用户的详细信息,而是当我们获取详细信息时才进行查询并返回AccountDetail对象。