1

I have a parent-child relation, with just one child on Sql Server. The child contains fat blob data I won't load. The child is optional and it must depend on the lifecycle of the parent. So the foreign key on the child, point to the parent and is unique.

I can use the official example on Hibernate reference.

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

    public virtual string Number { get; set; }

    public virtual PhoneDetails Details { get; set; }
}

public class PhoneDetails
{
    public virtual int Id { get; set; }

    public virtual Phone Phone { get; set; }

    public virtual string Provider { get; set; }
}

Phone details must depend on parent lifecycle on the database. I can use a one-to-one relation on sql with a unique foreign key on the child, or many-to-one relation with a simple foreign key (as in example) plus a unique constraint.

CREATE TABLE Phone (
    id BIGINT IDENTITY PRIMARY KEY,
    number VARCHAR(255)
)

CREATE TABLE PhoneDetails (
    id BIGINT IDENTITY PRIMARY KEY,
    phone_id BIGINT UNIQUE FOREIGN KEY REFERENCES dbo.Phone(id),
    provider VARCHAR(255)        
)

And this is fine i think:

enter image description here

So I have a Phone, this can live without details, and when I need I can add only one detail to complete my object with other details I don't want to load always.

How can I map these classes on NHibernate 5? It seems that i need to use HasOne, in a bidirectional way, but in that case I cannot use lazy load so every simple query on the Phone, will lead to a join and select of all the fields of the details.

I use the details table to store huge metadata that I must not need at 90% of time, but now everywhere I try to load a Phone, the query load also the huge details, this is really bad.

So in which way can I map this relation?

A parent, and a single child containing not useful fat data. In sql I think the structure is ok, because I don't want more children, and I want for them to live under the parent (so FK on child).

It is impossible that is not managed this type of relation, where am I wrong? database design? mapping?

This is the mapping I use:

  <class name="Phone" table="Phone">
    <id name="Id">
      <generator class="native"/>
    </id>
    <property name="Number"/>
    <one-to-one name="PhoneDetails" class="PhoneDetails"/>
  </class>

  <class name="PhoneDetails" table="PhoneDetails">
    <id name="Id">
      <generator class="native"/>
    </id>
    <property name="Provider" />
    <many-to-one name="Phone" column="phone_id" unique="true"/>    
  </class>

I have also tried with the second options, to use a foreign\primary key on the relational model (so i removed the FK and used the same Id as PK\FK), with this mapping in the child:

   <one-to-one name="Phone" class="Phone" constrained="true" />
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Luca Mazzanti
  • 619
  • 5
  • 10
  • Could you please share the code that you already have? In general hibernate will only load the child entity as soon as you access it. Do you not want to load the entity at all or do you just not want to load the blob data? – Dominik Sep 04 '18 at 18:00
  • I don't want to lazy load a property, i want lazy load on a 1-1 relation, thanks. I edited the post to add some informations, i did not put my mapping for now. It seems my only way to work is to work with a collection of details, having always just one item inside, but this is not good. – Luca Mazzanti Sep 05 '18 at 06:45
  • Please also provide your mappings. In the default-configuration nhibernate will not load the property Details unless you access it. – Dominik Sep 05 '18 at 13:21
  • I add the mapping. in the default configuration, in a one-to-one bidirectional relation, it will load also the detail. Reading deep the hibernate reference, it seems that one-to-one is considered only in one direction as an extension of the parent, where you can threat things as parent or load the detail+parent attached. – Luca Mazzanti Sep 05 '18 at 14:46
  • Hello Luca, I am struggling with the similar issue. Person HasOne PersonDetail witch is not mandatory. Everytime I list Persons all PersonDetails all selected in separate selects (so I am ending with N+1 select issue). Did you found any working solution? – user2126375 Sep 21 '18 at 09:01
  • It seems that hasone is used to extend a class so giù can retrieve the specialized One with a lazy mandatory parent. But It not work reverse. I removed the navigation property on parent and i work without that. No other solutions viable. – Luca Mazzanti Sep 22 '18 at 10:10

2 Answers2

1

According to this SO answer, Lazy-Loading is not supported on non-mandatory one-to-one relationships.

So, in order to work around this, why don't you get rid of this one-to-one relationship ? If you do not need the details 90% of the time, I would consider removing the relation-ship and have a seperate repository method that just enables you to retrieve the PhoneDetails for a specified Phone.

To continue with your example, I would end up with this:

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

    public virtual string Number { get; set; }
}

public class PhoneDetails
{
    public virtual int Id { get; set; }

    public virtual Phone BelongsToPhone { get; set; }

    public virtual string Provider { get; set; }
}

public class PhoneRepository
{
      public Phone GetPhone( long id ){}

      public PhoneDetails GetPhoneDetails( Phone phone ){}
}
Frederik Gheysels
  • 56,135
  • 11
  • 101
  • 154
  • And how is he going to know which `PhoneDetails` belongs to which `Phone` with your solution? You should leave the `Phone` in the `PhoneDetails` – Dominik Sep 05 '18 at 15:58
  • You can have a phone-id in the underlying datatable. Ok, the PhoneDetails entity should have this as well – Frederik Gheysels Sep 05 '18 at 19:47
  • Actually i take this way. I removed the bidirectional one-to-one. I kept it only in the details part. in the details, with the constrained property, H knows that a phone must exists and work in lazy mode with parent. But I had to loose my details on parent DTO. So if you want to edit your answer, you can add a Phone property in PhoneDetails. – Luca Mazzanti Sep 06 '18 at 05:59
0

A nullable one-to-one cannot know without querying the associated table whether it exists or not, thus for loading its proxy it is required to query the associated table. From that point, it has up to known been consider in past discussions to be better to query the whole state rather than just the key, thus the eager load. -> You won't be able to enable lazy loading on a reference that is not mandatory. At least not with a standard solution.

If you want the relation to be able to lazy load the PhoneDetails you will have to make it mandatory / not nullable.

As an alternative you could mark the properties in the PhoneDetails as lazy="true". Therefore the table will be accessed and a proxy will be created for the PhoneDetail-object as soon as you load the parent entity but all properties marked as lazy="true" will only be loaded as soon as you access them. Please note that ALL properties with lazy="true" will be loaded as soon as you access one of them

Dominik
  • 1,623
  • 11
  • 27
  • I collected the same informations. Unfortunatly a mandatory detail is not the need. Also to work on properties lazy is not so interesting. I thought there was a managed way to have a parent-child lazy in a 1-0..1 relation. – Luca Mazzanti Sep 06 '18 at 05:58