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='化学' and index(score) = s.Id and s.Name='小明'
这里出现了我们很少用到的hql函数:index()。这是专门应对map的,其目的是获得map的索引字段。
吊诡的是,虽然我们指明,map的key是对象,如Course.Students的key是Student的对象,但index()的结果,仍然是
即便是“简单的查询”,如查询小明所有课程的成绩,也有一个问题要注意。这个问题和懒加载相关。
在这个查询中,我们已经知道需要获取所有课程,因此,希望进行预先加载:
from Student s join fetch s.Courses course where s.Name='小明'
得到结果后,如果脱离查询的环境,如释放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所表达的,操作起来更为复杂。但这样的代价,我们乐意承担。
这是因为,我们更看重模型设计,更重视概念完整性。是模型决定具体实现,而不是反过来,根据具体实现来修改模型的设计。

MySQLは、初心者がデータベーススキルを学ぶのに適しています。 1.MySQLサーバーとクライアントツールをインストールします。 2。selectなどの基本的なSQLクエリを理解します。 3。マスターデータ操作:テーブルを作成し、データを挿入、更新、削除します。 4.高度なスキルを学ぶ:サブクエリとウィンドウの関数。 5。デバッグと最適化:構文を確認し、インデックスを使用し、選択*を避け、制限を使用します。

MySQLは、テーブル構造とSQLクエリを介して構造化されたデータを効率的に管理し、外部キーを介してテーブル間関係を実装します。 1.テーブルを作成するときにデータ形式と入力を定義します。 2。外部キーを使用して、テーブル間の関係を確立します。 3。インデックス作成とクエリの最適化により、パフォーマンスを改善します。 4.データベースを定期的にバックアップおよび監視して、データのセキュリティとパフォーマンスの最適化を確保します。

MySQLは、Web開発で広く使用されているオープンソースリレーショナルデータベース管理システムです。その重要な機能には、次のものが含まれます。1。さまざまなシナリオに適したInnodbやMyisamなどの複数のストレージエンジンをサポートします。 2。ロードバランスとデータバックアップを容易にするために、マスタースレーブレプリケーション機能を提供します。 3.クエリの最適化とインデックスの使用により、クエリ効率を改善します。

SQLは、MySQLデータベースと対話して、データの追加、削除、変更、検査、データベース設計を実現するために使用されます。 1)SQLは、ステートメントの選択、挿入、更新、削除を介してデータ操作を実行します。 2)データベースの設計と管理に作成、変更、ドロップステートメントを使用します。 3)複雑なクエリとデータ分析は、ビジネス上の意思決定効率を改善するためにSQLを通じて実装されます。

MySQLの基本操作には、データベース、テーブルの作成、およびSQLを使用してデータのCRUD操作を実行することが含まれます。 1.データベースの作成:createdatabasemy_first_db; 2。テーブルの作成:createTableBooks(idintauto_incrementprimarykey、titlevarchary(100)notnull、authorvarchar(100)notnull、published_yearint); 3.データの挿入:InsertIntoBooks(タイトル、著者、公開_year)VA

WebアプリケーションにおけるMySQLの主な役割は、データを保存および管理することです。 1.MYSQLは、ユーザー情報、製品カタログ、トランザクションレコード、その他のデータを効率的に処理します。 2。SQLクエリを介して、開発者はデータベースから情報を抽出して動的なコンテンツを生成できます。 3.MYSQLは、クライアントサーバーモデルに基づいて機能し、許容可能なクエリ速度を確保します。

MySQLデータベースを構築する手順には次のものがあります。1。データベースとテーブルの作成、2。データの挿入、および3。クエリを実行します。まず、createdAtabaseおよびcreateTableステートメントを使用してデータベースとテーブルを作成し、InsertINTOステートメントを使用してデータを挿入し、最後にSelectステートメントを使用してデータを照会します。

MySQLは、使いやすく強力であるため、初心者に適しています。 1.MYSQLはリレーショナルデータベースであり、CRUD操作にSQLを使用します。 2。インストールは簡単で、ルートユーザーのパスワードを構成する必要があります。 3.挿入、更新、削除、および選択してデータ操作を実行します。 4. Orderby、Where and Joinは複雑なクエリに使用できます。 5.デバッグでは、構文をチェックし、説明を使用してクエリを分析する必要があります。 6.最適化の提案には、インデックスの使用、適切なデータ型の選択、優れたプログラミング習慣が含まれます。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

AtomエディタMac版ダウンロード
最も人気のあるオープンソースエディター

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい
