初识Hibernate之关联映射(二)

一、基于外键的单向一对一关联映射
     具有一对一关联的表结构也是很常见的,比如:一个人对应于一张身份证。于是我们的person表会有一个外键关联到 idcard表的主键,只要这个外键列唯一即可保证person到idcard表的关系由多对一变为一对一,也就是说单向的一对一关联映射其实上也就是外键列唯一的多对一的关联映射。例如:

public class Person {
    private int id;
    private String name;
    private IdCard idcard;
    //省略get,set方法
}
public class IdCard {
    private int id;
    private String code;
    //省略get,set方法
}
<class name="Person" table="person">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="name"/>
    <many-to-one name="idcard" column="idcard_id" unique="true"/>
</class>

<class name="IdCard" table="idcard">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="code"/>
</class>

我们可以看到,几乎所有的代码和多对一关联映射都是差不多的,只不过在 many-to-one 元素中指定 unique="true",原来可以有多个具有相同外键值的记录映射到一的一端,现在指定外键值唯一之后,产生了唯一的一对一的关联映射。下面看看我们的测试代码:

//main 方法
Person p1 = new Person();
p1.setName("single");

IdCard idCard = new IdCard();
idCard.setCode("2345");

p1.setIdcard(idCard);

session.save(p1);
session.save(idCard);

我们分别创建两端的一个实例对象,然后由person端维护这种关联关系并将数据插入到数据库。看一眼结果:

这里写图片描述

这就是基于外键的单向一对一关联映射,与多对一的映射的唯一区别就在于,通过指定外键列唯一来让多的一端唯一,从而形成这种一对一的映射关系。

二、基于主键的单向一对一关联映射
     所谓基于主键的映射就是指,其中的一张表的主键值依赖于另一张表的主键值。还是我们的人和身份证模型:

这里写图片描述

像这种IDCard表完全可以作为属性字段追加到person表的后面的情况(主键重复可覆盖),我们叫这种关联映射为基于主键的一对一关联映射。看看配置文件:

/*person实体类的映射文件的配置*/
<class name="Person" table="person">
    <id name="id" column="pId">
        <generator class="foreign">
            <param name="property">idCard</param>
        </generator>
    </id>
    <property name="name"/>
    <one-to-one name="idCard" constrained="true"/>
</class>
/*IDCard实体类的映射文件的配置*/
<class name="IdCard" table="idCard">
    <id name="id">
        <generator class="native"/>
    </id>
    <property name="code"/>
</class>

可以看到,主要的变化还是在于实体类person。首先我们指定他的主键不再自增,而是由外键约束到其他表,对应的其他表的类型则是自己实体类的idCard属性对应的表。one-to-one标签则配置了person类中的IdCard属性,便于我们在取数据的时候Hibernate填充数据到该属性中。下面我们添加数据到数据库中,通过查看输出的Sql语句了解Hibernate是如何为我们创建关联的。

Person person = new Person();
person.setName("single");

IdCard idCard = new IdCard();
idCard.setCode("23456789");
        
person.setIdCard(idCard);

session.save(idCard);
session.save(person);

这里写图片描述

显然,我们对于idcard表的主键指定了native自增,而对于person表的主键并没有指定自增,person表的主键依赖于idcard的主键。我们并不用指定person表的主键值,因为person会根据和自己关联的idcard表的主键来为自己的主键赋值。

三、单向多对多关联映射
     多对多的表关联类型也是非常常见的,例如:

这里写图片描述

很常见的一个例子,一个学生可以有多个老师,同时一个老师也可以有多个学生,那么这就是很明显的多对多的关联映射。针对这种情况,一般来说数据库的表可以设计如下:

这里写图片描述

但是这种的表结构设计将会直接导致student表大量冗余,虽然解决了这种多对多的表级关联,但是存在大量冗余。Hibernate中处理这种多对多的关联关系是通过引入另一张表来实现对两个表主键的关联进而关联了两张表。

这里写图片描述

Student表和teacher表之间的多对多关联完全由connect表进行体现,各自表中数据不再大量冗余,这才是一种比较清晰的表结构设计。下面我们看代码:

public class Student {
    private int id;
    private String sName;
    private Set<Teacher> teachers = new HashSet<Teacher>(0);
    //省略get,set方法
}
public class Teacher {
    private int id;
    private String tName;
    //省略get,set方法
}

这是两张表对应的实体类,下面我们主要看映射文件的配置。

/*配置实体类Student*/
<class name="Student" table="student">
     <id name="id">
        <generator class="native"/>
     </id>
    <property name="sName"/>
    <set name="teachers" table="connect" cascade="save-update">
        <key column="stuId"></key>
        <many-to-many class="Teacher" column="tId"/>
    </set>
</class>
/*配置实体类Teacher*/
<class name="Teacher" table="teacher">
    <id name="id">
       <generator class="native"/>
    </id>
    <property name="tName"/>
</class>

Teacher实体类的配置并没有什么特殊需要解释的地方,而对于Student实体类的配置却稍有变化。其中的set标签就负责创建一张新表并负责关联两张表,table属性就是用于连接两张表主键值的表名(connect)。对于用于连接的表,有两个字段,一个是Student表的id,一个是teacher表的id,那么key标签就用于指定Student表id对应connect表中的名称,teacher表的id对应到connect表的名称则由many-to-many标签的column属性指定。下面我们插入多条数据:

这里写图片描述

显然,通过抽出connect表连关联两张表,对于Student和teacher表的表结构来说是更加简洁清晰的。

至此,有关Hibernate中关联映射的内容已经简单介绍完毕,虽然以后会更多的使用注解来配置这些映射关联,但是都是基于XML的,对于新手来说,学习XML配置关联映射是有助于理解注解配置。总结不到之处,望指出,下篇我们学习集合的映射。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值