Enterprise Java Development@TOPIC@

Chapter 56. One-to-One Relationships

56.1. One-to-One: Uni-directional
56.2. @OneToOne Annotation
56.3. @JoinColumn Annotation
56.4. @JoinColumns Annotation
56.5. One-to-One: Bi-directional
56.6. Bi-directional Relationships and Ownership
56.7. Other Topics
56.8. Summary

Figure 56.1. One-to-One Uni-directional

One-to-One Uni-directional

Figure 56.2. One-to-One Bi-directional

One-to-One Bi-directional

Uni-directional

Only one class ("owner") knows of the relationship

  • Uses the @OneToOne annotation

  • Defines mapping to database

    • Uses either @JoinColumn or @JoinTable

Bi-directional

Both classes know of the relationship

  • Both classes use the @OneToOne annotation

  • One class is considered the owner and maps relation to the database

    • Uses either @JoinColumn or @JoinTable

    • Changes here change the database

  • One class is considered the inverse and names the other entity's property

    • @OneToOne(mappedBy="owning-property")

    • Changes here do *not* change database


  • Relation realized through a foreign key

  • Foreign key represented by a separate column

  • Foreign key from owning entity table references primary key of inverse entity table


Figure 56.5. One-to-One Uni-directional Usage

  • Form the Relationship

    
    
    //create the owning side 
    ejava.examples.orm.rel.annotated.Person person = new Person();
    person.setFirstName("john");
    person.setLastName("doe");
    person.setPhone("410-555-1212");
    //create the inverse side 
    ejava.examples.orm.rel.annotated.Photo photo = new Photo();        
    photo.setImage(image);        
    //create the person and photo detached
    assertEquals(0, person.getId());
    assertEquals(0, photo.getId());
    //add photo to person and persist object tree
    person.setPhoto(photo); //this sets the FK in person
    logger.info("added photo to person:{}", person);
    em.persist(person);        
    assertNotEquals("personId not set", 0, person.getId());
    assertNotEquals("photoId not set", 0, photo.getId());
    logger.info("created person:{}", person);
    logger.info("     and photo:{}", photo);
    em.flush();
  • Output

    -added photo to person:Person@7ab802f4, id=0, name=john doe, phone=410-555-1212, 
        photo=Photo@608cd501, id=0. image=46080 bytes
    -
        call next value for hibernate_sequence
    -
        call next value for hibernate_sequence
    -Photo@608cd501: getId()=2
    -created person:Person@7ab802f4, id=1, name=john doe, phone=410-555-1212, 
        photo=Photo@608cd501, id=2. image=46080 bytes
    -     and photo:Photo@608cd501, id=2. image=46080 bytes
    -
        insert
        into
            ORMREL_PHOTO
            (image, PHOTO_ID)
        values
            (?, ?)
    -
        insert
        into
            ORMREL_PERSON
            (firstName, lastName, phone, PERSON_PHOTO, PERSON_ID)
        values
            (?, ?, ?, ?, ?)
    
    • Person and Photo are given an ID during the persist

    • Photo is inspected for its ID to be assigned to Person FK to Photo

    • Rows (with foreign key) are inserted into database on next flush cycle

  • Find Object with Relationship in Database

    //verify what we can get from DB
    
    em.flush(); em.clear();
    Person person2 = em.find(Person.class, person.getId());
    assertNotNull(person2);
    assertNotNull(person2.getPhoto());
    logger.info("found person:{}", person2);
  • Output

    -
        select
            person0_.PERSON_ID as PERSON_I1_27_0_,
            person0_.firstName as firstNam2_27_0_,
            person0_.lastName as lastName3_27_0_,
            person0_.phone as phone4_27_0_,
            person0_.PERSON_PHOTO as PERSON_P5_27_0_
        from
            ORMREL_PERSON person0_
        where
            person0_.PERSON_ID=?
    -Person@20cdb152, ctor()
    -Photo$HibernateProxy$2MfejJnf@0: ctor()
    -
        select
            photo0_.PHOTO_ID as PHOTO_ID1_28_0_,
            photo0_.image as image2_28_0_
        from
            ORMREL_PHOTO photo0_
        where
            photo0_.PHOTO_ID=?
    -Photo@57fdb8a4: ctor()
    -found person:Person@20cdb152, id=1, name=john doe, phone=410-555-1212, 
        photo=Photo@57fdb8a4, id=2. image=46080 bytes
    

Calls to em.clear() are for test purposes only!

Calls to em.clear() for are test purposes only and should not be a common thing in production code. We must clear the current instance from the cache if we want the provider to query the database for the rows versus pulling the existing instance from the cache. Calling em.clear() within production code will clear all instances from the persistence context and make your code have unwanted side-effects when called.

optional:boolean (default=true)

Designates whether relation is required. Default is true.

fetch:FetchType (default=EAGER)

Use EAGER or LAZY fetching of relationship when loading this entity. More of coverage of fetch in Fetching section

orphanRemoval:boolean (default=false)

Remote entity only exists for use by this relation. Automatically delete when relation terminated.

cascade:CascadeType[] (default=none)

Perform actions taken on this entity on related entity

targetEntity:Class

Define type for related class (if related Java type over-generalized)

mappedBy:String

Used by inverse side to specify owning entity property that maps relation to DB

Defines a foreign key mapping

Used to define multiple @JoinColumns when using composite foreign keys

@OneToOne

@JoinColumns({ //defines an array of @JoinColumns
    @JoinColumn(...),
    @JoinColumn(...)    
})

  • No additional foreign key used to satisfy the bi-directional aspect of relation



Note

Notice only owning entity table is updated when relationship formed.