NHibernate使用唯一外键进行一对一映射

时间:2015-12-17 02:31:11

标签: linq nhibernate

我通过一对一的映射看到了一些意外的查询行为,其中" child"记录可能为空。当看起来左连接更合适时,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(请注意&#34;其中&#34;子句可防止任何不具备关联员工记录的客户记录完全退回):

    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

我希望它生成如下内容,以便在记录不存在时返回null EmployeeId:

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

我是否遗漏了某些东西,或者这是一个已知的问题,与#34;一对一&#34;映射?

1 个答案:

答案 0 :(得分:0)

我们已经在投影中使用子查询确定了一种不同的查询方法。我认为潜在的问题与此处的公开问题有关:https://nhibernate.jira.com/browse/NH-3117?jql=text%20~%20%22one%20to%20one%22。似乎NHibernate linq提供程序没有正确处理引用项可能不存在的一对一映射。

解决方法:

DateTimePicker.Format = DateTimePickerFormat.Custom
DateTimePicker.CustomFormat = "dd-MM-yyyy"

生成以下SQL:

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();