Hibernate中的一对一关联技术

本文探讨了一对一数据库关系的设计与实现,特别是在Hibernate框架下如何通过XML配置和注解实现这种关系,包括数据的增删改查操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

假如现在有一个用户的注册功能,这个功能可能要涉及到10个字段的填写,那么这个时候如果上来就填写10个信息,那么可能注册的人就少了。现在的开发往往都会提供一个快速注册和一个信息维护。那么这种情况就可以用一对一关系来描述。
【定义数据库的创建脚本】

DROP TABLE IF EXISTS member_details;
DROP TABLE IF EXISTS member_login;

CREATE TABLE member_login(
   mid          VARCHAR(50),
   password     VARCHAR(32),
   CONSTRAINT pk_mid PRIMARY KEY(mid)
);

CREATE TABLE member_details(
   mid          VARCHAR(50),
   name         VARCHAR(50),
   email        VARCHAR(50),
   phone        VARCHAR(50),
   CONSTRAINT pk_mid2 PRIMARY KEY(mid),
   CONSTRAINT fk_mid FOREIGN KEY(mid) REFERENCES member_login(mid) ON DELETE CASCADE
);

很明显看出,一张表为主表,而另一张表为子表,但是在子表里面mid这个列不允许重复必须是外键,那么就表示一个主表的数据只能和一个字表的数据关联在一起。

基于*.hbm.xml文件的实现

如果要想实现一对一的关系就必须同时选择好两张关联的表。
【观察MemberLogin.java类和MemberDetails.java类】

@SuppressWarnings("serial")
public class MemberLogin implements Serializable {
    private String mid;
    private String password;
    private MemberDetails memberDetails = new MemberDetails();
    //方法略
}
@SuppressWarnings("serial")
public class MemberDetails implements Serializable {
    private String mid;
    private String name;
    private String email;
    private String phone;
    private MemberLogin memberLogin = new MemberLogin();
    //方法略
}

一对一关联仅靠类的关联关系是远远不足的,还需要涉足到配置文件之中。
【观察MemberLogin.hbm.xml文件】

    <class name="com.gub.vo.MemberLogin" table="member_login" schema="company">
        <id name="mid" column="mid"/>
        <property name="password" column="password"/>
        <!--明确表示了此处要配置一对一的映射关系-->
        <!--
            name:表示类中的属性
            class:表示属性对应的类型
        -->
        <one-to-one name="memberDetails" class="com.gub.vo.MemberDetails" />
    </class>

【观察MemberDeatils.hbm.xml文件】

    <class name="com.gub.vo.MemberDetails" table="member_details" schema="company">
        <id name="mid" column="mid"/>
        <property name="name" column="name"/>
        <property name="email" column="email"/>
        <property name="phone" column="phone"/>
        <!--
            一对一映射关系,其中
            name:表示MemberDetails中的属性
            class:属性对应的类型
            constrained:约束属性
        -->
        <one-to-one name="memberLogin" class="com.gub.vo.MemberLogin" constrained="true" />
    </class>

在配置one-to-one关系的时候,有时候会出现一个“constraint=true”的配置,此属性表示先增加主表数据,再增加子表的数据。(如果没有此配置,则表示先增加子表数据,后再增加主表数据)
【数据增加】

    public static void main(String[] args) {
        MemberLogin login = new MemberLogin();
        login.setMid("用户9871574255");
        login.setPassword("123456");
        HibernateSessionFactory.getSession().save(login);
        HibernateSessionFactory.getSession().beginTransaction().commit();
        HibernateSessionFactory.closeSession();
    }


发现栈溢出了,因此需要修改MemberLogin.java和MebmerDetails.java类,去掉关键字new。再次执行发现增加成功(因为没有涉及到MemberDetails数据对象的保存,所以只保存了MemberLogin的数据)。

【保存完整信息】

    public static void main(String[] args) {
        MemberLogin login = new MemberLogin();
        login.setMid("用户9871574255");
        login.setPassword("123456");
        MemberDetails details = new MemberDetails();
        details.setMid("用户9871574255");
        details.setEmail("123@789.com");
        details.setName("没名字");
        details.setPhone("没电话");
        details.setMemberLogin(login);
        login.setMemberDetails(details);
        HibernateSessionFactory.getSession().save(login);
        HibernateSessionFactory.getSession().beginTransaction().commit();
        HibernateSessionFactory.closeSession();
    }

因为Hibetnate不支持数据级联操作(如果此时执行程序,只会增加主表数据,子表并不会增加),所以必须修改MemberLogin.hbm.xml文件,加入数据集连的配置。

    <!--
        one-to-one:明确表示了此处要配置一对一的映射关系
        name:表示类中的属性
        class:表示属性对应的类型
        cascade:表示MemberLogin操作的时候MemberDetails要一起进行级联操作
    -->
    <one-to-one name="memberDetails" class="com.gub.vo.MemberDetails" cascade="all" />

执行代码发现数据增加成功:

观察控制台输出发现,Hibernate在执行增加操作时先查询member_details表中的数据,如果不存在则进行增加,如果存在则进行修改。查询后首先增加主表数据,主表数据增加完成之后再增加子表数据。
对于数据的修改分为两种情况:如果存在member_details表的数据如何修改?如果member_details表的数据不存在又如何修改?
【数据修改】

    public static void main(String[] args) {
        MemberLogin login = new MemberLogin();
        login.setMid("用户9871574255");
        login.setPassword("hahaha");
        MemberDetails details = new MemberDetails();
        details.setMid("用户9871574255");
        details.setEmail("123@789.com");
        details.setName("名字-1");
        details.setPhone("电话-1");
        details.setMemberLogin(login);
        login.setMemberDetails(details);
        HibernateSessionFactory.getSession().update(login);
        HibernateSessionFactory.getSession().beginTransaction().commit();
        HibernateSessionFactory.closeSession();
    }

如果原来没有MemberDetails数据,在运行程序之后会为其添加数据:

如果原本已经存在MemberDetails对应的数据,现在完整更新:

通过观察发现,Hibernate在执行update()方法时先查询判断子表中存不存在指定的数据,若子表没有对应的数据,则发出增加子表数据的操作,随后发出跟更新主表数据的操作,若查询到子表存在有对应的数据,则先更新主表的数据,随后在更新子表的数据。
【数据查询】

    public static void main(String[] args) {
        MemberLogin memberLogin = (MemberLogin)HibernateSessionFactory.getSession().get(MemberLogin.class,"用户98715f74255");
        System.out.println(memberLogin);
        HibernateSessionFactory.closeSession();
    }


MemberLogin{mid=‘用户98715f74255’, password=‘hahaha’, memberDetails=MemberDetails{mid=‘用户98715f74255’, name=‘名字-1’, email=‘123@789.com’, phone=‘电话-1’}}
通过观察sql语句发现,虽然数据查询成功,但是用的是多表查询(Hibernate使用的查询模式就是多表查询),性能较低。
【修改查询模式】
在MemberLogin.hbm.xml文件中修改fetch属性,fetch属性主要是指表级联数据的处理模式,有两种方式:

属性值解释
fetch=“join”使用多表连接进行组合查询
fetch=“select”使用单查询实现
         <one-to-one 
                name="memberDetails" 
                class="com.gub.vo.MemberDetails" 
                cascade="all" 
                fetch="select"/>


观察SQL语句发现变成了两个查询,第一个查询的是主表,第二个查询的是子表。
【使用Query接口查询】

    public static void main(String[] args) {
        String hql = "FROM MemberLogin WHERE mid=? ";
        Query query = HibernateSessionFactory.getSession().createQuery(hql);
        query.setParameter(0,"用户9871574255");
        MemberLogin login = (MemberLogin)query.uniqueResult();
        System.out.println(login);
        HibernateSessionFactory.closeSession();
    }

执行代码后发现执行的sql语句与使用get()方法查询相同,一对一关系虽然分为了两张数据表,但是只有一行数据。

基于Annotation的配置

【配置MemberLogin类】
MemberLogin类中的注解按照常规配置即可,主要是在getMemberDetails()方法上添加OneToOne注解

    @OneToOne(fetch=FetchType.LAZY,mappedBy = "memberLogin",cascade = CascadeType.ALL)
    public MemberDetails getMemberDetails() {
        return memberDetails;
    }

【配置MemberDetails类】
MemberDetails表是MemberLogin的子表,同理需要在getMemberLogin()方法上配置注解

    @OneToOne(fetch = FetchType.LAZY)
    @PrimaryKeyJoinColumn
    public MemberLogin getMemberLogin() {
        return memberLogin;
    }

执行以上全部测试方法,发现与hbm.xml文件配置一值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值