hibernate的OneToOne映射

hibernate的OneToOne映射

1、前言

@OneToOne注解可以建立实体bean之间的一对一的关联。 一对一关联有四种情况:

一是关联的实体都共享同样的主键;

二是其中一个实体通过外键关联到另一个实体的主键 (注意要模拟一对一关联必须在外键列上添加唯一约束);

三是通过关联表来保存两个实体之间的连接关系 (注意要模拟一对一关联必须在每一个外键上添加唯一约束);

以上三种情况在源码中可见说明,但其实在实际开发过程中,总会有第四种情况:两个实体类只是通过两个普通字段关联(外键关联的特殊案例)。

2、共享主键

通过共享主键来进行一对一关联映射。

单向关联

entity:Body
@Entity
@Setter
@Getter
@Table(name = "jei_body")
public class Body implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "name")
    @ApiModelProperty(value = "姓名")
    private String name;

    @Column(name = "gender")
    @ApiModelProperty(value = "性别")
    private Integer gender;

    @OneToOne(fetch = FetchType.LAZY)
    @PrimaryKeyJoinColumn
    private Heart heart;

}

注意:

@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn

使用注解@PrimaryKeyJoinColumn定义了一对一关联。

双向关联

entity:Heart
@Entity
@Setter
@Getter
@Table(name = "jei_heart")
public class Heart implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "is_health")
    @ApiModelProperty(value = "是否健康")
    private String isHealth;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "heart")
    private Body body;

}

注意:

@OneToOne(fetch = FetchType.LAZY, mappedBy = "heart")

这里就不再需要@PrimaryKeyJoinColumn进行定义关联,需要用mappedBy 来确定关系由谁来维护。mappedBy="heart"说明在两个关联的实体Bean中,body这一端是关系的拥有者。如果mappedBy没有写的话,hibernate会自动在heart表中生成body_id字段(关联表名_id),否则运行代码就会报错:Unknown column 'heart0_.body_id' in 'field list'

其它

查询body

查询heart

3、外键关联

通过外键关联需要区别主表和副表(要求主表的非主键列(如果它是主键的话,就是”共享主键“)是副表的主键),当然它们是可以对调角色的,只是观察的角度不同。

单向关联

entity:Person
@Entity
@Setter
@Getter
@Table(name = "jei_person")
public class Person implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "name")
    @ApiModelProperty(value = "姓名")
    private String name;

    @Column(name = "card_id")
    @ApiModelProperty(value = "身份证id")
    private Long cardId;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "card_id", referencedColumnName = "id", insertable = false, updatable = false)
    private Card card;

}

注意:

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "card_id", referencedColumnName = "id", insertable = false, updatable = false)

这里的name=“card_id”, 指的当前表的字段(可以是任意名字的字段),referencedColumnName = "id"是副表的主键id,其它这里可以不写referencedColumnName ,因为它默认而且必须是副表的主键id。

双向关联

entity:Card
@Entity
@Setter
@Getter
@Table(name = "jei_card")
public class Card implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "county_name")
    @ApiModelProperty(value = "县、市辖区、县级市、自治县、旗、自治旗、特区、林区名称")
    private String countyName;

    @Column(name = "detail")
    @ApiModelProperty(value = "身份证明细")
    private String detail;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "card")
    private Person person;

}

注意:

@OneToOne(fetch = FetchType.LAZY, mappedBy = "card")

这里副表就不再需要@JoinColumn进行声明关联,需要用mappedBy 来确定关系由谁来维护。mappedBy="card"说明在两个关联的实体Bean中,person这一端是关系的拥有者,person一方的表中生成到关联类的外键。

其它

它们分别关联查询的sql截图。

查询person

查询card

4、关联表

通过关联表来保存两个实体之间的连接关系,也许是最另类的,也是很常见的。

单向关联

entity:Customer
@Entity
@Setter
@Getter
@Table(name = "jei_customer")
public class Customer implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "name")
    @ApiModelProperty(value = "姓名")
    private String name;

    @Column(name = "gender")
    @ApiModelProperty(value = "性别")
    private Integer gender;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinTable(name = "jei_customer_and_passport",
            joinColumns = @JoinColumn(name = "customer_id"),
            inverseJoinColumns = @JoinColumn(name = "passport_id"))
    private Passport passport;

}

注意:

@OneToOne(fetch = FetchType.LAZY)

@JoinTable(name = "jei_customer_and_passport",joinColumns = @JoinColumn(name = "customer_id"),inverseJoinColumns = @JoinColumn(name = "passport_id"))

Customer通过名为 jei_customer_and_passport表和 Passport关联,该关联表(jei_customer_and_passport)拥有名为passport_id的外键列,该 外键指向Passport,该信息定义为inverseJoinColumn的属性值,而customer_id外键列指向Customer, 该信息定义为 joinColumns的属性值。

双向关联

entity:Passport
@Entity
@Setter
@Getter
@Table(name = "jei_passport")
public class Passport implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "country")
    @ApiModelProperty(value = "国家")
    private String country;

    @Column(name = "is_effect")
    @ApiModelProperty(value = "是否有效")
    private Integer isEffect;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "passport")
    private Customer customer;

}

注意:

@OneToOne(fetch = FetchType.LAZY, mappedBy = "passport")

这里不需要@JoinTable进行声明关联,需要用mappedBy 来确定关系由谁来维护。mappedBy="passport"说明在两个关联的实体Bean中,customer这一端是关系的拥有者。

其它

查询customer

查询passport

5、只是普通字段关联

它其实也是一种外键关联,只是说关联字段没有一个是主键;这样的关联关系,推荐只做单向关联。

单向关联

entity:Teacher
@Entity
@Setter
@Getter
@Table(name = "jei_teacher")
public class Teacher implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "name")
    @ApiModelProperty(value = "姓名")
    private String name;

    @Column(name = "course")
    @ApiModelProperty(value = "课程")
    private String course;

    @Column(name = "student_relation_id")
    @ApiModelProperty(value = "关系id")
    private Long studentRelationId;

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "student_relation_id", referencedColumnName = "teacher_relation_id", insertable = false, updatable = false)
    @NotFound(action = NotFoundAction.IGNORE)
    private Student student;

}

注意:

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "student_relation_id", referencedColumnName = "teacher_relation_id", insertable = false, updatable = false)
@NotFound(action = NotFoundAction.IGNORE)

这里的name=“student_relation_id”, 指的当前表的字段,referencedColumnName = "teacher_relation_id"是关联表的字段,推荐要加上@NotFound(action = NotFoundAction.IGNORE),这时一定是@OneToOne(fetch = FetchType.EAGER),否则有会警告。

双向关联

entity:Student
@Entity
@Setter
@Getter
@Table(name = "jei_student")
public class Student implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    @ApiModelProperty(value = "id")
    private Long id;

    @Column(name = "name")
    @ApiModelProperty(value = "姓名")
    private String name;

    @Column(name = "address")
    @ApiModelProperty(value = "地址")
    private String address;

    @Column(name = "teacher_relation_id")
    @ApiModelProperty(value = "关系id")
    private Long teacherRelationId;

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "teacher_relation_id", referencedColumnName = "student_relation_id", insertable = false, updatable = false)
    @NotFound(action = NotFoundAction.IGNORE)
    private Teacher teacher;

//    这种是要报错的
//    @OneToOne(fetch = FetchType.LAZY, mappedBy = "student")
//    private Teacher teacher;

}

注意:

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "teacher_relation_id", referencedColumnName = "student_relation_id", insertable = false, updatable = false)
@NotFound(action = NotFoundAction.IGNORE)

这里的name=“teacher_relation_id”, 指的当前表的字段,referencedColumnName = "student_relation_id"是关联表的字段,推荐要加上@NotFound(action = NotFoundAction.IGNORE),这时一定是@OneToOne(fetch = FetchType.EAGER),否则有会警告。

其它

单向关联查询teacher

查询teacher
select teacher0_.id as id1_10_, teacher0_.course as course2_10_, teacher0_.name as name3_10_, teacher0_.student_relation_id as student_4_10_ from jei_teacher teacher0_ where teacher0_.id=1 and (teacher0_.name like ?) and (teacher0_.course like ?) and teacher0_.student_relation_id=100

select student0_.id as id1_9_2_, student0_.address as address2_9_2_, student0_.name as name3_9_2_, student0_.teacher_relation_id as teacher_4_9_2_, teacher1_.id as id1_10_0_, teacher1_.course as course2_10_0_, teacher1_.name as name3_10_0_, teacher1_.student_relation_id as student_4_10_0_, student2_.id as id1_9_1_, student2_.address as address2_9_1_, student2_.name as name3_9_1_, student2_.teacher_relation_id as teacher_4_9_1_ from jei_student student0_ left outer join jei_teacher teacher1_ on student0_.teacher_relation_id=teacher1_.student_relation_id left outer join jei_student student2_ on teacher1_.student_relation_id=student2_.teacher_relation_id where student0_.teacher_relation_id=?

select teacher0_.id as id1_10_2_, teacher0_.course as course2_10_2_, teacher0_.name as name3_10_2_, teacher0_.student_relation_id as student_4_10_2_, student1_.id as id1_9_0_, student1_.address as address2_9_0_, student1_.name as name3_9_0_, student1_.teacher_relation_id as teacher_4_9_0_, teacher2_.id as id1_10_1_, teacher2_.course as course2_10_1_, teacher2_.name as name3_10_1_, teacher2_.student_relation_id as student_4_10_1_ from jei_teacher teacher0_ left outer join jei_student student1_ on teacher0_.student_relation_id=student1_.teacher_relation_id left outer join jei_teacher teacher2_ on student1_.teacher_relation_id=teacher2_.student_relation_id where teacher0_.student_relation_id=?
查询student
select student0_.id as id1_9_, student0_.address as address2_9_, student0_.name as name3_9_, student0_.teacher_relation_id as teacher_4_9_ from jei_student student0_ where student0_.id=1 and (student0_.name like ?) and (student0_.address like ?) and student0_.teacher_relation_id=100

select teacher0_.id as id1_10_2_, teacher0_.course as course2_10_2_, teacher0_.name as name3_10_2_, teacher0_.student_relation_id as student_4_10_2_, student1_.id as id1_9_0_, student1_.address as address2_9_0_, student1_.name as name3_9_0_, student1_.teacher_relation_id as teacher_4_9_0_, teacher2_.id as id1_10_1_, teacher2_.course as course2_10_1_, teacher2_.name as name3_10_1_, teacher2_.student_relation_id as student_4_10_1_ from jei_teacher teacher0_ left outer join jei_student student1_ on teacher0_.student_relation_id=student1_.teacher_relation_id left outer join jei_teacher teacher2_ on student1_.teacher_relation_id=teacher2_.student_relation_id where teacher0_.student_relation_id=?

select student0_.id as id1_9_2_, student0_.address as address2_9_2_, student0_.name as name3_9_2_, student0_.teacher_relation_id as teacher_4_9_2_, teacher1_.id as id1_10_0_, teacher1_.course as course2_10_0_, teacher1_.name as name3_10_0_, teacher1_.student_relation_id as student_4_10_0_, student2_.id as id1_9_1_, student2_.address as address2_9_1_, student2_.name as name3_9_1_, student2_.teacher_relation_id as teacher_4_9_1_ from jei_student student0_ left outer join jei_teacher teacher1_ on student0_.teacher_relation_id=teacher1_.student_relation_id left outer join jei_student student2_ on teacher1_.student_relation_id=student2_.teacher_relation_id where student0_.teacher_relation_id=?

注意:普通字段的双向关联,就导致了查询sql多执行了一条并且是复杂查询,因此也推荐尽量使用单向关联。

posted @   南翔技校毕业后  阅读(146)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示