2015-12-17 51 views
0

我看到一些意外的查詢行爲與一對一的映射,其中「子」記錄可能爲空。當看起來像左連接更合適時,Nhibernate似乎生成內連接。鑑於以下模式:NHibernate與唯一外鍵的一對一映射

CREATE TABLE [dbo].[Customer](
    [CustomerId] [int] IDENTITY(1,1) NOT NULL, 
    [PersonId] [int] NOT NULL, 
    [AccountNumber] [int] NOT NULL, 
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
(
    [CustomerId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 


CREATE TABLE [dbo].[Employee](
    [EmployeeId] [int] IDENTITY(1,1) NOT NULL, 
    [PersonId] [int] NOT NULL, 
    [Title] [varchar](50) NOT NULL, 
CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED 
(
    [EmployeeId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 


CREATE TABLE [dbo].[Person](
    [PersonId] [int] IDENTITY(1,1) NOT NULL, 
    [Name] [varchar](50) NOT NULL, 
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED 
(
    [PersonId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 


ALTER TABLE [dbo].[Customer] WITH CHECK ADD CONSTRAINT [FK_Customer_Person] FOREIGN KEY([PersonId]) 
REFERENCES [dbo].[Person] ([PersonId]) 
GO 
ALTER TABLE [dbo].[Customer] CHECK CONSTRAINT [FK_Customer_Person] 
GO 
ALTER TABLE [dbo].[Employee] WITH CHECK ADD CONSTRAINT [FK_Employee_Person] FOREIGN KEY([PersonId]) 
REFERENCES [dbo].[Person] ([PersonId]) 
GO 
ALTER TABLE [dbo].[Employee] CHECK CONSTRAINT [FK_Employee_Person] 
GO 

與以下類:

namespace OneToOneMapping.Model 
{ 
    public class Person 
    { 
     public virtual int Id { get; set; } 
     public virtual string Name { get; set; } 
     public virtual Customer Customer { get; set; } 
     public virtual Employee Employee { get; set; } 
    } 
    public class Employee 
    { 
     public virtual System.Int32 Id { get; set; } 
     public virtual Person Person { get; set; } 
     public virtual string Title { get; set; } 
    } 
    public class Customer 
    { 
     public virtual System.Int32 Id { get; set; } 
     public virtual Person Person { get; set; } 
     public virtual int AccountNumber { get; set; } 
    } 
} 

和映射:

<?xml version="1.0" encoding="utf-8" ?> 
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
        assembly="OneToOneMapping" 
        namespace="OneToOneMapping.Model"> 

    <class name="Person" table="Person" lazy="true" > 
    <id name="Id" column="PersonId"> 
     <generator class="identity"/> 
    </id> 
    <one-to-one name="Customer" cascade="all-delete-orphan" property-ref="Person" class="OneToOneMapping.Model.Customer" /> 
    <one-to-one name="Employee" cascade="all-delete-orphan" property-ref="Person" class="OneToOneMapping.Model.Employee" /> 
    <property name="Name"/> 
    </class> 

    <class name="OneToOneMapping.Model.Customer, OneToOneMapping" table="Customer"> 
    <id name="Id" column="CustomerId"> 
     <generator class="identity"/> 
    </id> 
    <property name="AccountNumber"/> 
    <many-to-one name="Person" class="Person" column="PersonId" unique="true" cascade="save-update"/> 
    </class> 

    <class name="OneToOneMapping.Model.Employee, OneToOneMapping" table="Employee" lazy="true" mutable="true"> 
    <id name="Id" column="EmployeeId"> 
     <generator class="identity"/> 
    </id> 
    <property name="Title"/> 
    <many-to-one name="Person" class="Person" column="PersonId" unique="true" cascade="save-update"/> 
    </class> 
</hibernate-mapping> 

我無法創建LINQ查詢在那裏我可以返回一個投影指示是否客戶也是員工(反之亦然)。運行像下面的語句:

var t = Session.Query<Customer>().Select(c => new { AccountNumber = c.AccountNumber, Name= c.Person.Name, IsEmployee = c.Person.Employee.Id != null }).ToList(); 

生成以下SQL(注意「其中」條款阻止不也有客戶的任何記錄都無法返回所有相關的員工記錄):

SELECT customer0_.AccountNumber AS col_0_0_ 
    ,person1_.NAME AS col_1_0_ 
    ,employee2_.EmployeeId AS col_2_0_ 
FROM Customer customer0_ 
LEFT JOIN Person person1_ ON customer0_.PersonId = person1_.PersonId 
    ,Employee employee2_ 
WHERE person1_.PersonId = employee2_.PersonId 

我希望它產生類似下面只返回一個空僱員時,記錄不存在:

SELECT customer0_.AccountNumber AS col_0_0_ 
    ,person1_.NAME AS col_1_0_ 
    ,employee2_.EmployeeId AS col_2_0_ 
FROM Customer customer0_ 
LEFT JOIN Person person1_ ON customer0_.PersonId = person1_.PersonId 
Left Join Employee employee2_ on person1_.PersonId = employee2_.PersonId 

我缺少的東西或者這是「一對一」映射的已知問題?

+0

我有點困惑你的域模型。你是說一個人有一個客戶或一個員工,或者你是否想說一個人是一個客戶還是一個員工?如果是後者,則需要使用繼承映射,而不是一對一映射。 – Fran

+0

理想情況下,這將是一種繼承模式,但這不起作用,因爲一個人既可以是員工又可以是**客戶。這將打破多態查詢,如Session.Get (id)。相反,我們認爲_person_是員工和客戶共同的屬性_have_。 – Riggins

+0

這將是任何映射和基類查詢將返回兩個子類。 – Fran

回答

0

我們已經確定了一種不同的查詢方法,在似乎可行的投影中使用子查詢。我相信潛在的問題與這裏的公開問題有關:https://nhibernate.jira.com/browse/NH-3117?jql=text%20~%20%22one%20to%20one%22。看起來,NHibernate的linq提供程序不能正確處理引用項目可能不存在的一對一映射。

解決方法:

var t = Session.Query<Customer>() 
       .Select(c => new { 
        AccountNumber = c.AccountNumber, 
        Name= c.Person.Name, 
        IsEmployee = Session.Query<Employee>().Any(e => e.Id == c.Person.Id) 
       }).ToList(); 

生成以下SQL:

SELECT customer0_.AccountNumber AS col_0_0_ 
    ,person1_.NAME AS col_1_0_ 
    ,CASE 
     WHEN EXISTS (
       SELECT employee2_.EmployeeId 
       FROM Employee employee2_ 
       WHERE employee2_.EmployeeId = customer0_.PersonId 
       ) 
      THEN 1 
     ELSE 0 
     END AS col_2_0_ 
FROM Customer customer0_ 
LEFT JOIN Person person1_ ON customer0_.PersonId = person1_.PersonId