Table of Contents
1、引言
2、映射
3、查询
4、维护
5、结论
Home Database Mysql Tutorial 用NHibernate处理带属性的多对多关系

用NHibernate处理带属性的多对多关系

Jun 07, 2016 pm 04:04 PM
relation deal with introduction

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; }
}
Copy after login

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; }
}
Copy after login

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

    public virtual string Name { get; set; }

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

原来的列表(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>
Copy after login

经过这样的映射,多对多关系中的属性带到了类中,而且避免了为关系创建实体——没有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;
Copy after login

这里出现了我们很少用到的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;
Copy after login

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

s.Courses.Select(c => c.Key.Name)
Copy after login
仍会抛出异常。因为,上述的查询并没有把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();
}
Copy after login

其中,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>
Copy after login

但在包含属性的多对多关系上,由于要使用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();
}
Copy after login

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

5、结论

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

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

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

The operation process of WIN10 service host occupying too much CPU The operation process of WIN10 service host occupying too much CPU Mar 27, 2024 pm 02:41 PM

1. First, we right-click the blank space of the taskbar and select the [Task Manager] option, or right-click the start logo, and then select the [Task Manager] option. 2. In the opened Task Manager interface, we click the [Services] tab on the far right. 3. In the opened [Service] tab, click the [Open Service] option below. 4. In the [Services] window that opens, right-click the [InternetConnectionSharing(ICS)] service, and then select the [Properties] option. 5. In the properties window that opens, change [Open with] to [Disabled], click [Apply] and then click [OK]. 6. Click the start logo, then click the shutdown button, select [Restart], and complete the computer restart.

Learn how to handle special characters and convert single quotes in PHP Learn how to handle special characters and convert single quotes in PHP Mar 27, 2024 pm 12:39 PM

In the process of PHP development, dealing with special characters is a common problem, especially in string processing, special characters are often escaped. Among them, converting special characters into single quotes is a relatively common requirement, because in PHP, single quotes are a common way to wrap strings. In this article, we will explain how to handle special character conversion single quotes in PHP and provide specific code examples. In PHP, special characters include but are not limited to single quotes ('), double quotes ("), backslash (), etc. In strings

Python package manager sinkhole pitfalls: how to avoid them Python package manager sinkhole pitfalls: how to avoid them Apr 01, 2024 am 09:21 AM

The python package manager is a powerful and convenient tool for managing and installing Python packages. However, if you are not careful when using it, you may fall into various traps. This article describes these pitfalls and strategies to help developers avoid them. Trap 1: Installation conflict problem: When multiple packages provide functions or classes with the same name but different versions, installation conflicts may occur. Response: Check dependencies before installation to ensure there are no conflicts between packages. Use pip's --no-deps option to avoid automatic installation of dependencies. Pitfall 2: Old version package issues: If a version is not specified, the package manager may install the latest version even if there is an older version that is more stable or suitable for your needs. Response: Explicitly specify the required version when installing, such as p

Java JSP Security Vulnerabilities: Protect Your Web Applications Java JSP Security Vulnerabilities: Protect Your Web Applications Mar 18, 2024 am 10:04 AM

JavaServerPages (jsP) is a Java technology used to create dynamic WEB applications. JSP scripts are executed on the server side and rendered to html on the client side. However, JSP applications are susceptible to various security vulnerabilities that can lead to data leakage, code execution, or denial of service. Common security vulnerabilities 1. Cross-site scripting (XSS) XSS vulnerabilities allow attackers to inject malicious scripts into web applications, which will be executed when the victim accesses the page. Attackers can use these scripts to steal sensitive information (such as cookies and session IDs), redirect users, or compromise pages. 2. Injection Vulnerability An injection vulnerability allows an attacker to query a web application’s database

Getting Started with Java Git: A Beginner's Guide to Version Control Getting Started with Java Git: A Beginner's Guide to Version Control Mar 27, 2024 pm 02:21 PM

A version control system (VCS) is an indispensable tool in software development that allows developers to track and manage code changes. git is a popular and powerful VCS that is widely used in Java development. This guide will introduce the basic concepts and operations of Git, providing Java developers with the basics of version control. The basic concept of Git Repository: where code and version history are stored. Branch: An independent line of development in a code base that allows developers to make changes without affecting the main line of development. Commit: A change to the code in the code base. Rollback: Revert the code base to a previous commit. Merge: Merge changes from two or more branches into a single branch. Getting Started with Git 1. Install Git Download and download from the official website

The role of Python ORM in artificial intelligence and machine learning The role of Python ORM in artificial intelligence and machine learning Mar 18, 2024 am 09:10 AM

Python object-relational mapping (ORM) is a technology that allows seamless interaction between Python objects and relational database tables. In artificial intelligence (AI) and machine learning (ML) applications, ORM plays a vital role, simplifying data access and management, and improving development efficiency. Data storage and management ORM provides an object-oriented interface to access and operate databases. In AI and ML projects, large amounts of data usually need to be processed, including training data sets, model parameters, and prediction results. ORM allows developers to interact with this data in a simple and understandable way without having to worry about the underlying SQL syntax. This significantly reduces development time and the possibility of errors. For example, when using Tensorfl

The future of concurrent collections in Java: Exploring new features and trends The future of concurrent collections in Java: Exploring new features and trends Apr 03, 2024 am 09:20 AM

With the rise of distributed systems and multi-core processors, concurrent collections have become crucial in modern software development. Java concurrent collections provide efficient and thread-safe collection implementations while managing the complexity of concurrent access. This article explores the future of concurrent collections in Java, focusing on new features and trends. New feature JSR354: Resilient concurrent collections jsR354 defines a new concurrent collection interface with elastic behavior to ensure performance and reliability even under extreme concurrency conditions. These interfaces provide additional features of atomicity, such as support for mutable invariants and non-blocking iteration. RxJava3.0: Reactive Concurrent Collections RxJava3.0 introduces the concept of reactive programming, enabling concurrent collections to be easily integrated with reactive data flows.

The philosophy of Java file operations: understanding the nature of files The philosophy of Java file operations: understanding the nature of files Mar 21, 2024 pm 03:20 PM

Files are the basic unit of information storage and management in computer systems, and are also the core focus of Java file operations. Understanding the nature of files is critical to operating and managing them effectively. Abstraction and Hierarchy A file is essentially an abstraction that represents a set of data stored in a persistent medium such as disk or memory. The logical structure of a file is usually defined by the operating system and provides a mechanism for organizing and accessing data. In Java, files are represented by the File class, which provides abstract access to the file system. Data Persistence One of the key characteristics of a file is its data persistence. Unlike data in memory, data in files persists even after the application exits. This persistence makes files useful for long-term storage and sharing of information.

See all articles