初识Hibernate之关联映射(一)

  上篇文章我们对持久化对象进行的学习,了解了它的三种不同的状态并通过它完成对数据库的映射操作。但这都是基于单张表的操作,如果两张或者两张以上的表之间存在某种关联,我们又该如何利用持久化对象进行操作呢?本篇主要介绍的关联映射就是针对有着某种关联的多张表的各种操作,主要涉及内容如下:

  • 组合主键的映射
  • 组件的映射
  • 单向多对一的映射
  • 单向一对多的映射
  • 双向一对多的映射
  • 级联映射

一、组合主键的映射操作
     根据我们的上篇文章,对于单一主键,在对象映射配置文件中使用 id标签即可完成配置。但是,往往有些主键并不是单一的,它可能由多个字段组合,那么此时就不能使用 id标签进行指定了。例如,我们有一张scores表,该表有三个字段,uid表示学生id,sub表示学生考试的学科,score表示该门考试成绩。那么确定一个学生的某门考试成绩就需要uid和usub,此时它们就是scores表的主键,因为它能唯一确定一行数据。

/*定义scores实体类*/
public class Scores {
    private Score scoreId;    //主键
    private int score;
    //省略get,set方法
}
public class Score implements Serializable {
    private int userId;
    private String sub;
    //省略get,set方法
}

在这里,我们定义scores实体类,我们将主键封装成一个类score,该类必须继承接口Serializable ,最好还能实现它的两个方法equals和hashcode。然后就是我们的实体映射配置文件的编写:

<class name="DbClasses.Scores" table="scores">
    <composite-id name="scoreId" class="DbClasses.Score">
        <key-property name="userId" column="userId"></key-property>
        <key-property name="sub" column="sub"></key-property>
    </composite-id>
    <property name="score" column="score"></property>
</class>

对于组合主键,我们使用标签composite-id来配置,name和class属性分别指定主键类在实体类中的名称及其位置。该标签下的key-property标签则是用来指定主键成员对应于数据表中的具体字段的。我们运行程序,看看Hibernate为我们创建的表中是否有一个组合主键:

这里写图片描述

显然,在我们的scores表中,userId和sub的组合构成了该表的主键。这就是组合主键在Hibernate中的配置情况,组合主键还是比较常见的。

二、组件映射
     这里将要介绍的组件映射和上述介绍的主键映射名称相似,但确实完全不同的概念,需要予以区别。假设我们有一张person表:

这里写图片描述

person表中有主键id,name,age字段,还有三个地址字段(往往一个人有多个地址,我们要分别进行保存)。但是这样的一个表结构对应于我们的实体类如下:

public class Person {
    private int id;
    private String name;
    private int age;
    private String address1;
    private String address2;
    private String address3;
    //省略get,set方法
}

对于这样的实体类来说,我们觉得他对于地址字段的处理是冗余的,假如某个人有十个地址,难道要在我们的实体类中配置十个属性吗?显然是不合理的,Hibernate允许我们像主键映射一样将所有的地址字段抽象出来一个类。

public class Person {
    private int id;
    private String name;
    private int age;
    private Address address;
    //省略get,set方法
}
public class Address {
    private String address1;
    private String address2;
    private String address3;
    //省略get,set方法
}

重点在于我们的实体映射文件的配置,

<class name="DbClasses.Person" table="person">
    <id name="id" column="id">
        <generator class="native"></generator>
    </id>
    <property name="name" column="name"></property>
    <property name="age" column="age"></property>
    <!--映射的一个组件属性-->
    <component name="address" class="DbClasses.Address">
        <property name="address1" column="address1"></property>
        <property name="address2" column="address2"></property>
        <property name="address3" column="address3"></property>
    </component>
</class>

实体类中其他的属性照常配置,对于这个Address类型的属性,我们使用component标签进行配置,name和class分别指定组件名和其位置,在该标签下,使用property标签配置组件的成员对应于数据表中的字段。然后我们删除表,重新看看这次Hibernate为我们生成的表结构:

这里写图片描述

显然结果是一样的,我们使用组件映射的一个好处就在于在这个实体类中,对于数据表结构显得非常清晰,代码的封装性更好,方便查错。

三、单向多对一的映射
     以上介绍的两种基本映射并不属于我们本篇将要介绍的关联映射,关联映射就是指在处理多张有关联的表时,我们的实体类的配置。所谓的多对一就是指,其中一张表的主键是另一张表的外键,例如:

这里写图片描述

我们有一张Student表,一张grade表,其中grade表的主键id是Student表的外键(grade),Student中的多条记录对应于grade的一条记录,所以这种表的关联又被称作多对一的关联关系。下面我们看看如何通过对实体类的配置达到构建这种多对一的数据表关联。

public class Student {
    private int uId;
    private String name;
    private int age;
    private Grade grade;
    //省略get,set方法
}
public class Grade {
    private int id;
    private String grade;
    //省略get,set方法
}

Student和Grade分别对应不同数据库表的两个实体类,但是Student实体类中有一个属性grade指向实体类Grade。实体类映射配置文件如下:

<class name="DbClasses2.Student" table="student">
    <id name="uId" column="uid">
         <generator class="native"></generator>
    </id>
    <property name="name"></property>
    <property name="age"></property>
    <many-to-one name="grade" class="DbClasses2.Grade" column="grade_id"></many-to-one>
</class>

<class name="DbClasses2.Grade" table="grade">
    <id name="id" column="id">
        <generator class="native"></generator>
    </id>
    <property name="grade" column="grade"></property>
</class>

Grade实体类的配置没什么变化,Student中使用many-to-one标签将本实体类中属性grade配置指向另一个实体类Grade,并用column指定外键名称。也就是当Hibernate根据映射配置文件创建数据表的时候,发现属性grade指向的是一个实体类Grade,于是把Grade表的主键关联到grade字段上。我们先运行程序看看HIbernate是否为我们创建了这种外键关联,然后通过插入数据进一步理解Hibernate在底层为我们做的事情。

这里写图片描述

显然,在分别创建Student和Grade表之后,Hibernate又向数据库发送了一条alter语句,该语句负责添加外键关联。下面我们看看能否利用外键获取到Grade表中的成绩。

/*首先向表中插入信息*/
Student student = new Student();
student.setName("single");
student.setAge(21);

Grade grade = new Grade();
grade.setGrade("优秀");

student.setGrade(grade);

session.save(grade);
session.save(student);

我们知道,一个实体类的对象对应于数据表的一条记录,那么grade代表Grade表的一条记录,而该对象作为属性值被赋值给Student中的grade属性则表示它将自己的引用交给了Student的外键字段,也就是说student这条记录可以通过外键字段找到grade代表的这条记录。有点绕,但是学过数据库原理的应该不难理解。下面我们看,如何利用外键获取对应的Grade表中的一条完整记录。

Student student = (Student)session.get(Student.class,1);
Grade grade = student.getGrade();
System.out.println(grade.getId()+":"+grade.getGrade());

输出结果:

1:优秀

显然,我们通过Student返回的grade对象代表的就是基于Student外键字段值在Grade表中的一条数据。

四、单向一对多的映射
     单向many-to-one关联是最常见的单向关联关系,其逻辑也趋近与我们的Sql语言,还算比较好理解。而对于单向一对多的映射则是其的一个逆向的逻辑,相对而言比较难以理解。这个多对一和一对多之间有个很明显的区别,对于多对一的情况,我们在得到Student对象代表的一条数据记录时,可以利用外键得到相对应Grade表中的一条记录。但是反过来,如果我们想知道对于Grade表的某条记录究竟有多少Student表记录予以对应呢?起码这是多对一无法直接解决的,那么我们的一对多则着重解决的就是这么一个问题。

     所谓的一对多就是利用一的一方完成这种外键关联的构建。我们先看实体类的定义:

/*student实体类的定义*/
  • 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、付费专栏及课程。

余额充值