찾다
데이터 베이스MySQL 튜토리얼用NHibernate处理带属性的多对多关系

1、引言 老谭在面试开发人员的时候,为了考察他们的数据库开发能力,常常祭出我的法宝,就是大学数据库教程中讲到的一个模式:学生选课。这个模式是这样的: 在这个模式中,学生(Student)和课程(Course)都是实体,分别有主键Id。考试成绩(Score)是学生和课程

1、引言

老谭在面试开发人员的时候,为了考察他们的数据库开发能力,常常祭出我的法宝,就是大学数据库教程中讲到的一个模式:学生选课。这个模式是这样的:

\

在这个模式中,学生(Student)和课程(Course)都是实体,分别有主键Id。考试成绩(Score)是学生和课程之间的多对多关系。

基于这个模式,对于新手,可以出一些简单查询要求,对于熟手,可以出一些复杂的查询要求,用起来得心应手。

但今天要说的重点是,怎么用NHibernate实现这个模式。和一般多对多关系稍有不同的是,这个关系带有一个属性,就是考试成绩(Score)。

如果多对多关系上带有属性,我们一般会把这个关系扩充为实体,也就是把一个多对多关系转换为一个实体加上两个多对一关系。

如果多对多关系上有多个属性,将其转换为一个实体还是有必要的,但只有很少的属性(本例中只有一个),转换为实体实在有些浪费。因为一般情况下,对于实体,我们要为其创建一个人工主键,有了人工主键,又要创建一个序列。在映射时,这个实体自然要有对应的类。这一大堆事情,想想就非常麻烦。

问题的关键是,在概念上这本来就是一个关系,为了实现的方便,而将其转换为一个实体,这个凭空多出来的实体,使概念变得复杂,非常别扭。

因此,这里要探究一下,让关系恢复为为关系,怎么用NHibernate来处理。

2、映射

如果关系表Score中没有Score那个属性字段,Student实体可以映射为这样的类:

public class Student
{
    public virtual long Id { get; set; }

    public virtual string Name { get; set; }

    public virtual IList<Course> Courses { get; set; }
}

Course也类似。

但有了属性Score,再这样映射就把这个属性丢了。为了带上Score属性,两个实体应该这样映射:

public class Student
{
    public virtual long Id { get; set; }

    public virtual string Name { get; set; }

    public virtual IDictionary<Course, int> Courses { get; set; }
}

 

public class Course
{
    public virtual long Id { get; set; }

    public virtual string Name { get; set; }

    public virtual IDictionary<Student, int> Students { get; set; }
}

原来的列表(List)变成了字典(Dictionary),字典的主键是原来列表中的元素,值则是关系上的属性值,即考试成绩。

对应的映射文件自然也要调整,结果如下:

...
<class name="Course" table="Course">

  <id name="Id" unsaved-value="0">
    <column name="Id" />
    ...
  </id>

  <property name="Name">
    <column name="Name"/>
  </property>

  <map name="Students" table="Score" lazy="true">
    <key column="CourseId"/>
    <index-many-to-many column="StudentId" class="Student" />
    <element column="Score" />
  </map>
</class>...
<class name="Student" table="Student">
     
  <id name="Id" unsaved-value="0">
      <column name="Id" /> 
      ...
  </id>

  <property name="Name">
    <column name="Name"/>
  </property>

  <map name="Courses" table="Score" lazy="true">
    <key column="StudentId"/>
    <index-many-to-many column="CourseId" class="Course"/>
    <element column="Score" />
  </map>
</class>

经过这样的映射,多对多关系中的属性带到了类中,而且避免了为关系创建实体——没有Score这样的类。对这样映射结果的操作,和常规多对多关系映射方式多生成的基本类似,但也有几个要注意的问题。

3、查询

对于一些简单的查询,如:

小明所有课程的成绩;化学这门课所有学生的成绩等

都比较容易处理,因为这只需要在一个实体上加过滤条件。

如果需要在两个实体上加过滤条件,如查询小明化学课的成绩,就有些复杂,其查询语句Hql是这样的:

select score
  from Course c, Student s
  join c.Students score
 where c.Name=&#39;化学&#39;
   and index(score) = s.Id
   and s.Name=&#39;小明&#39;

这里出现了我们很少用到的hql函数:index()。这是专门应对map的,其目的是获得map的索引字段。

吊诡的是,虽然我们指明,map的key是对象,如Course.Students的key是Student的对象,但index()的结果,仍然是 中 column字段的值。在上面的查询中,index(score),我们期望的结果是Student对象,但结果却是对象的Id。因此,在查询语句中,我们不得不关联上Student s,并利用 s.Name 进行过滤。 

即便是“简单的查询”,如查询小明所有课程的成绩,也有一个问题要注意。这个问题和懒加载相关。

在这个查询中,我们已经知道需要获取所有课程,因此,希望进行预先加载:

 from Student s
 join fetch s.Courses course
where s.Name=&#39;小明&#39;

得到结果后,如果脱离查询的环境,如释放Session,在访问课程时,如:

s.Courses.Select(c => c.Key.Name)
仍会抛出异常。因为,上述的查询并没有把Course对象加载进来。
目前还不知道怎么预加载map中索引对象。需要的话,只有依赖懒加载机制。

4、维护

除了查询,在对关系进行维护时,也有一点值得特别注意:save-update类型的cascade无效。

cascade属性,为我们进行关系维护带来不少便利。在常规(不带属性)的多对多关系中,我们的维护操作可以是这样的:

小明选化学课:

using (var tx = session.BeginTransaction())
{
    var student = GetStudent(studentName) ??
                  new Student { Name = studentName, Courses = new List<Course>() };

    var course = GetCourse(courseName) ??
                  new Course { Name = courseName, Students = new List<Student>() };

    if (!course.Students.Contains(student))
    {
        course.Students.Add(student);
    }

    session.SaveOrUpdate(course);

    tx.Commit();
}

其中,GetStudent(studentName) 和 GetCourse(courseName) 分别是指,根据学生名字或课程名字从数据库中加载相应对象。在上面的代码片段中,如果数据库中没有,则新建。维护关系后,进行保存。

需要注意的是,在保存 course 的时候,并没有确保 student 是一个持久化的对象。如果student尚未被持久化,则在保存时,NHibernate会自动保存,并维护和course的关系。能够这么做,依赖于Course中关系上的cascade属性定义(第三行末尾):

  <class name="Course" table="Course">
    ...
    <bag name="Students" table="Scoure" lazy="true" cascade="save-update">
      <key column="CourseId"/>
      <many-to-many column="StudentId" class="Student" />
    </bag>
  </class>

但在包含属性的多对多关系上,由于要使用map,就无法进行这样的配置——配置了也不生效。如果数据库中尚未保存该学生,我们不得不首先创建并将其持久化(第7行):

using (var tx = session.BeginTransaction())
{
    var student = GetStudent(studentName);
    if (student == null)
    {
        student = new Student {Name = studentName, Courses = new Dictionary<Course, int>()};
        session.Save(student);
    }

    var course = GetCourse(courseName) ??
                 new Course {Name = courseName, Students = new Dictionary<Student, int>()};

    if (course.Students.ContainsKey(student))
    {
        course.Students[student] = score;
    }
    else
    {
        course.Students.Add(student, score);
    }

    session.SaveOrUpdate(course);

    tx.Commit();
}

否则,就等着NHibernate抛出异常吧。

5、结论

用Map/Dicitionary表达的多对多关系,要比用Bag/List所表达的,操作起来更为复杂。但这样的代价,我们乐意承担。

这是因为,我们更看重模型设计,更重视概念完整性。是模型决定具体实现,而不是反过来,根据具体实现来修改模型的设计。

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
Composite Index와 여러 단일 열 인덱스를 언제 사용해야합니까?Composite Index와 여러 단일 열 인덱스를 언제 사용해야합니까?Apr 11, 2025 am 12:06 AM

데이터베이스 최적화에서 쿼리 요구 사항에 따라 인덱싱 전략을 선택해야합니다. 1. 쿼리에 여러 열이 포함되고 조건 순서가 수정되면 복합 인덱스를 사용하십시오. 2. 쿼리에 여러 열이 포함되어 있지만 조건 순서가 고정되지 않은 경우 여러 단일 열 인덱스를 사용하십시오. 복합 인덱스는 다중 열 쿼리를 최적화하는 데 적합한 반면 단일 열 인덱스는 단일 열 쿼리에 적합합니다.

MySQL에서 느린 쿼리를 식별하고 최적화하는 방법은 무엇입니까? (느린 쿼리 로그, Performance_schema)MySQL에서 느린 쿼리를 식별하고 최적화하는 방법은 무엇입니까? (느린 쿼리 로그, Performance_schema)Apr 10, 2025 am 09:36 AM

MySQL 느린 쿼리를 최적화하려면 SlowQueryLog 및 Performance_Schema를 사용해야합니다. 1. SlowQueryLog 및 Set Stresholds를 사용하여 느린 쿼리를 기록합니다. 2. Performance_schema를 사용하여 쿼리 실행 세부 정보를 분석하고 성능 병목 현상을 찾고 최적화하십시오.

MySQL 및 SQL : 개발자를위한 필수 기술MySQL 및 SQL : 개발자를위한 필수 기술Apr 10, 2025 am 09:30 AM

MySQL 및 SQL은 개발자에게 필수적인 기술입니다. 1.MySQL은 오픈 소스 관계형 데이터베이스 관리 시스템이며 SQL은 데이터베이스를 관리하고 작동하는 데 사용되는 표준 언어입니다. 2.MYSQL은 효율적인 데이터 저장 및 검색 기능을 통해 여러 스토리지 엔진을 지원하며 SQL은 간단한 문을 통해 복잡한 데이터 작업을 완료합니다. 3. 사용의 예에는 기본 쿼리 및 조건 별 필터링 및 정렬과 같은 고급 쿼리가 포함됩니다. 4. 일반적인 오류에는 구문 오류 및 성능 문제가 포함되며 SQL 문을 확인하고 설명 명령을 사용하여 최적화 할 수 있습니다. 5. 성능 최적화 기술에는 인덱스 사용, 전체 테이블 스캔 피하기, 조인 작업 최적화 및 코드 가독성 향상이 포함됩니다.

MySQL 비동기 마스터 슬레이브 복제 프로세스를 설명하십시오.MySQL 비동기 마스터 슬레이브 복제 프로세스를 설명하십시오.Apr 10, 2025 am 09:30 AM

MySQL 비동기 마스터 슬레이브 복제는 Binlog를 통한 데이터 동기화를 가능하게하여 읽기 성능 및 고 가용성을 향상시킵니다. 1) 마스터 서버 레코드는 Binlog로 변경됩니다. 2) 슬레이브 서버는 I/O 스레드를 통해 Binlog를 읽습니다. 3) 서버 SQL 스레드는 데이터를 동기화하기 위해 Binlog를 적용합니다.

MySQL : 쉽게 학습하기위한 간단한 개념MySQL : 쉽게 학습하기위한 간단한 개념Apr 10, 2025 am 09:29 AM

MySQL은 오픈 소스 관계형 데이터베이스 관리 시스템입니다. 1) 데이터베이스 및 테이블 작성 : CreateAbase 및 CreateTable 명령을 사용하십시오. 2) 기본 작업 : 삽입, 업데이트, 삭제 및 선택. 3) 고급 운영 : 가입, 하위 쿼리 및 거래 처리. 4) 디버깅 기술 : 확인, 데이터 유형 및 권한을 확인하십시오. 5) 최적화 제안 : 인덱스 사용, 선택을 피하고 거래를 사용하십시오.

MySQL : 데이터베이스에 대한 사용자 친화적 인 소개MySQL : 데이터베이스에 대한 사용자 친화적 인 소개Apr 10, 2025 am 09:27 AM

MySQL의 설치 및 기본 작업에는 다음이 포함됩니다. 1. MySQL 다운로드 및 설치, 루트 사용자 비밀번호를 설정하십시오. 2. SQL 명령을 사용하여 CreateAbase 및 CreateTable과 같은 데이터베이스 및 테이블을 만듭니다. 3. CRUD 작업을 실행하고 삽입, 선택, 업데이트, 명령을 삭제합니다. 4. 성능을 최적화하고 복잡한 논리를 구현하기 위해 인덱스 및 저장 절차를 생성합니다. 이 단계를 사용하면 MySQL 데이터베이스를 처음부터 구축하고 관리 할 수 ​​있습니다.

InnoDB 버퍼 풀은 어떻게 작동하며 성능에 중요한 이유는 무엇입니까?InnoDB 버퍼 풀은 어떻게 작동하며 성능에 중요한 이유는 무엇입니까?Apr 09, 2025 am 12:12 AM

innodbbufferpool은 데이터와 색인 페이지를 메모리에로드하여 MySQL 데이터베이스의 성능을 향상시킵니다. 1) 데이터 페이지가 버퍼 풀에로드되어 디스크 I/O를 줄입니다. 2) 더러운 페이지는 정기적으로 디스크로 표시되고 새로 고침됩니다. 3) LRU 알고리즘 관리 데이터 페이지 제거. 4) 읽기 메커니즘은 가능한 데이터 페이지를 미리로드합니다.

MySQL : 초보자를위한 데이터 관리의 용이성MySQL : 초보자를위한 데이터 관리의 용이성Apr 09, 2025 am 12:07 AM

MySQL은 설치가 간단하고 강력하며 데이터를 쉽게 관리하기 쉽기 때문에 초보자에게 적합합니다. 1. 다양한 운영 체제에 적합한 간단한 설치 및 구성. 2. 데이터베이스 및 테이블 작성, 삽입, 쿼리, 업데이트 및 삭제와 같은 기본 작업을 지원합니다. 3. 조인 작업 및 하위 쿼리와 같은 고급 기능을 제공합니다. 4. 인덱싱, 쿼리 최적화 및 테이블 파티셔닝을 통해 성능을 향상시킬 수 있습니다. 5. 데이터 보안 및 일관성을 보장하기위한 지원 백업, 복구 및 보안 조치.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

Atom Editor Mac 버전 다운로드

Atom Editor Mac 버전 다운로드

가장 인기 있는 오픈 소스 편집기

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

에디트플러스 중국어 크랙 버전

에디트플러스 중국어 크랙 버전

작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음