Java Persistence/OneToOne

From Wikibooks, open books for an open world
Jump to: navigation, search

 

 

OneToOne[edit]

ObjectRelational-OneToOne.jpg

A OneToOne relationship in Java is where the source object has an attribute that references another target object and (if) that target object had the inverse relationship back to the source object it would also be a OneToOne relationship. All relationships in Java and JPA are unidirectional, in that if a source object references a target object there is no guarantee that the target object also has a relationship to the source object. This is different than a relational database, in which relationships are defined through foreign keys and querying such that the inverse query always exists.

JPA also defines a ManyToOne relationship, which is similar to a OneToOne relationship except that the inverse relationship (if it were defined) is a OneToMany relationship. The main difference between a OneToOne and a ManyToOne relationship in JPA is that a ManyToOne always contains a foreign key from the source object's table to the target object's table, where as a OneToOne relationship the foreign key may either be in the source object's table or the target object's table. If the foreign key is in the target object's table JPA requires that the relationship be bi-directional (must be defined in both objects), and the source object must use the mappedBy attribute to define the mapping.

In JPA a OneToOne relationship is defined through the @OneToOne annotation or the <one-to-one> element. A OneToOne relationship typically requires a @JoinColumn or @JoinColumns if a composite primary key.

Example of a OneToOne relationship database[edit]

EMPLOYEE (table)

EMP_ID FIRSTNAME LASTNAME SALARY ADDRESS_ID
1 Bob Way 50000 6
2 Sarah Smith 60000 7

ADDRESS (table)

ADDRESS_ID STREET CITY PROVINCE COUNTRY P_CODE
6 17 Bank St Ottawa ON Canada K2H7Z5
7 22 Main St Toronto ON Canada L5H2D5

Example of a OneToOne relationship annotations[edit]

@Entity
public class Employee {
  @Id
  @Column(name="EMP_ID")
  private long id;
  ...
  @OneToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="ADDRESS_ID")
  private Address address;
  
}

Example of a OneToOne relationship XML[edit]

<entity name="Employee" class="org.acme.Employee" access="FIELD">
    <attributes>
        <id name="id">
            <column name="EMP_ID"/>
        </id>
        <one-to-one name="address" fetch="LAZY">
            <join-column name="ADDRESS_ID"/>
        </one-to-one>
    </attributes>
</entity>

Inverse Relationships, Target Foreign Keys and Mapped By[edit]

A typical object centric perspective of a OneToOne relationship has the data model mirror the object model, in that the source object has a pointer to the target object, so the database source table has a foreign key to the target table. This is not how things always work out in the database though, in fact many database developers would think having the foreign key in the target table to be logical, as this enforces the uniqueness of the OneToOne relationship. Personally I prefer the object perspective, however you will most likely encounter both.

To start considering a bi-directional OneToOne relationship, you do not require two foreign keys, one in each table, so a single foreign key in the owning side of the relationship is sufficient. In JPA the inverse OneToOne must use the mappedBy attribute (with some exceptions), this makes the JPA provider use the foreign key and mapping information in the source mapping to define the target mapping.

See also, Target Foreign Keys, Primary Key Join Columns, Cascade Primary Keys.

The following gives an example of what the inverse address relationship would look like.

Example of an inverse OneToOne relationship annotations[edit]

@Entity
public class Address {
  @Id
  @Column(name = "ADDRESS_ID")
  private long id;
  ...
  @OneToOne(fetch=FetchType.LAZY, mappedBy="address")
  private Employee owner;
  ...
}

Example of an inverse OneToOne relationship XML[edit]

<entity name="Address" class="org.acme.Address" access="FIELD">
    <attributes>
        <id name="id"/>
        <one-to-one name="owner" fetch="LAZY" mapped-by="address"/>
    </attributes>
</entity>

See Also[edit]

Common Problems[edit]

Foreign key is also part of the primary key.[edit]
See Primary Keys through OneToOne Relationships.
Foreign key is also mapped as a basic.[edit]
If you use the same field in two different mappings, you typically require to make one of them read-only using insertable, updatable = false.
See Target Foreign Keys, Primary Key Join Columns, Cascade Primary Keys.
Constraint error on insert.[edit]
This typically occurs because you have incorrectly mapped the foreign key in a OneToOne relationship.
See Target Foreign Keys, Primary Key Join Columns, Cascade Primary Keys.
It can also occur if your JPA provider does not support referential integrity, or does not resolve bi-directional constraints. In this case you may either need to remove the constraint, or use EntityManager flush() to ensure the order your objects are written in.
Foreign key value is null[edit]
Ensure you set the value of the object's OneToOne, if the OneToOne is part of a bi-directional OneToOne relationship, ensure you set OneToOne in both objects, JPA does not maintain bi-directional relationships for you.
Also check that you defined the JoinColumn correctly, ensure you did not set insertable, updatable = false or use a PrimaryKeyJoinColumn, or mappedBy.

Advanced[edit]

Target Foreign Keys, Primary Key Join Columns, Cascade Primary Keys[edit]

If a OneToOne relationship uses a target foreign key (the foreign key is in the target table, not the source table), then JPA requires that you define a OneToOne mapping in both directions, and that the target foreign key mapping use the mappedBy attribute. The reason for this, is the mapping in the source object only affects the row the JPA writes to the source table, if the foreign key is in the target table, JPA has no easy way to write this field.

There are other ways around this problem however. In JPA the JoinColumn defines an insertable and updatable attribute, these can be used to instruct the JPA provider that the foreign key is actually in the target object's table. With these enabled JPA will not write anything to the source table, most JPA providers will also infer that the foreign key constraint is in the target table to preserve referential integrity on insertion. JPA also defines the @PrimaryKeyJoinColumn that can be used to define the same thing. You still must map the foreign key in the target object in some fashion though, but could just use a Basic mapping to do this.

Some JPA providers may support an option for a unidirectional OneToOne mapping for target foreign keys.

Target foreign keys can be tricky to understand, so you might want to read this section twice. They can get even more complex though. If you have a data model that cascades primary keys then you can end up with a single OneToOne that has both a logical foreign key, but has some fields in it that are logically target foreign keys.

경축! 아무것도 안하여 에스천사게임즈가 새로운 모습으로 재오픈 하였습니다.
어린이용이며, 설치가 필요없는 브라우저 게임입니다.
https://s1004games.com

For example consider Company, Department, Employee. Company's id is COM_ID, Department's id is a composite primary key of COM_ID and DEPT_ID, and Employee's id is a composite primary key of COM_ID, DEP_ID, and EMP_ID. So for an Employee its relationship to company uses a normal ManyToOne with a foreign key, but its relationship to department uses a ManyToOne with a foreign key, but the COM_ID uses insertable, updatable = false or PrimaryKeyJoinColumn, because it is actually mapped through the company relationship. The Employee's relationship to its address then uses a normal foreign key for ADD_ID but a target foreign key for COM_ID, DEP_ID, and EMP_ID.

This may work in some JPA providers, others may require different configuration, or not support this type of data model.

Example of cascaded primary keys database[edit]

COMPANY(table)

COM_ID NAME
1 ACME
2 Wikimedia

DEPARTMENT(table)

COM_ID DEP_ID NAME
1 1 Billing
1 2 Research
2 1 Accounting
2 2 Research

EMPLOYEE (table)

COM_ID DEP_ID EMP_ID NAME MNG_ID ADD_ID
1 1 1 Bob Way null 1
1 1 2 Joe Smith 1 2
1 2 1 Sarah Way null 1
1 2 2 John Doe 1 2
2 1 1 Jane Doe null 1
2 2 1 Alice Smith null 1

ADDRESS(table)

COM_ID DEP_ID ADD_ID ADDRESS
1 1 1 17 Bank, Ottawa, ONT
1 1 2 22 Main, Ottawa, ONT
1 2 1 255 Main, Toronto, ONT
1 2 2 12 Main, Winnipeg, MAN
2 1 1 72 Riverside, Winnipeg, MAN
2 2 1 82 Riverside, Winnipeg, MAN

Example of cascaded primary keys and mixed OneToOne and ManyToOne mapping annotations[edit]

@Entity
@IdClass(EmployeeId.class)
public class Employee {
  @Id
  @Column(name="EMP_ID")
  private long employeeId;
  @Id
  @Column(name="DEP_ID", insertable=false, updatable=false)
  private long departmentId;
  @Id
  @Column(name="COM_ID", insertable=false, updatable=false)
  private long companyId;
  ...
  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="COM_ID")
  private Company company;
  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumns({
    @JoinColumn(name="DEP_ID"),
    @JoinColumn(name="COM_ID", insertable=false, updatable=false)
  })
  private Department department;
  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumns({
    @JoinColumn(name="MNG_ID"),
    @JoinColumn(name="DEP_ID", insertable=false, updatable=false),
    @JoinColumn(name="COM_ID", insertable=false, updatable=false)
  })
  private Employee manager;
  @OneToOne(fetch=FetchType.LAZY)
  @JoinColumns({
    @JoinColumn(name="ADD_ID")
    @JoinColumn(name="DEP_ID", insertable=false, updatable=false),
    @JoinColumn(name="COM_ID", insertable=false, updatable=false)
  })
  private Address address;
  ...
}

Example of cascaded primary keys and mixed OneToOne and ManyToOne mapping annotations using PrimaryKeyJoinColumn[edit]

@Entity
@IdClass(EmployeeId.class)
public class Employee {
  @Id
  @Column(name="EMP_ID")
  private long employeeId;
  @Id
  @Column(name="DEP_ID", insertable=false, updatable=false)
  private long departmentId;
  @Id
  @Column(name="COM_ID", insertable=false, updatable=false)
  private long companyId;
  ...
  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="COM_ID")
  private Company company;
  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="DEP_ID")
  @PrimaryKeyJoinColumn(name="COM_ID")
  private Department department;
  @ManyToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="MNG_ID")
  @PrimaryKeyJoinColumns({
    @PrimaryKeyJoinColumn(name="DEP_ID")
    @PrimaryKeyJoinColumn(name="COM_ID")
  })
  private Employee manager;
  @OneToOne(fetch=FetchType.LAZY)
  @JoinColumn(name="ADD_ID")
  @PrimaryKeyJoinColumns({
    @PrimaryKeyJoinColumn(name="DEP_ID")
    @PrimaryKeyJoinColumn(name="COM_ID")
  })
  private Address address;
  ...
}

Mapping a OneToOne Using a Join Table[edit]

In some data models, you may have a OneToOne relationship defined through a join table. For example consider you had existing EMPLOYEE and ADDRESS tables with no foreign key, and wanted to define a OneToOne relationship without changing the existing tables. To do this you could define an intermediate table that contained the primary key of both objects. This is similar to a ManyToMany relationship, but if you add a unique constraint to each foreign key you can enforce that it is OneToOne (or even OneToMany).

JPA defines a join table using the @JoinTable annotation and <join-table> XML element. A JoinTable can be used on a ManyToMany or OneToMany mappings, but the JPA 1.0 specification is vague whether it can be used on a OneToOne. The JoinTable documentation does not state that it can be used in a OneToOne, but the XML schema for <one-to-one> does allow a nested <join-table> element. Some JPA providers may support this, and others may not.

If your JPA provider does not support this, you can workaround the issue by instead defining a OneToMany or ManyToMany relationship and just define a get/set method that returns/sets the first element on the collection.

Example of a OneToOne using a JoinTable database[edit]

EMPLOYEE (table)

EMP_ID FIRSTNAME LASTNAME SALARY
1 Bob Way 50000
2 Sarah Smith 60000

EMP_ADD(table)

EMP_ID ADDR_ID
1 6
2 7

ADDRESS (table)

ADDRESS_ID STREET CITY PROVINCE COUNTRY P_CODE
6 17 Bank St Ottawa ON Canada K2H7Z5
7 22 Main St Toronto ON Canada L5H2D5

Example of a OneToOne using a JoinTable[edit]

  @OneToOne(fetch=FetchType.LAZY)
  @JoinTable(
      name="EMP_ADD",
      joinColumns=
        @JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID"),
      inverseJoinColumns=
        @JoinColumn(name="ADDR_ID", referencedColumnName="ADDRESS_ID"))
  private Address address;
  ...

Example of simulating a OneToOne using a OneToMany JoinTable[edit]

  @OneToMany
  @JoinTable(
      name="EMP_ADD"
      joinColumns=
        @JoinColumn(name="EMP_ID", referencedColumnName="EMP_ID"),
      inverseJoinColumns=
        @JoinColumn(name="ADDR_ID", referencedColumnName="ADDRESS_ID"))
  private List<Address> addresses;
  ...
  public Address getAddress() {
    if (this.addresses.isEmpty()) {
      return null;
    }
    return this.addresses.get(0);
  }
  public void setAddress(Address address) {
    if (this.addresses.isEmpty()) {
      this.addresses.add(address);
    } else {
      this.addresses.set(0, address);
    }
  }
  ...

 

[출처] https://en.wikibooks.org/wiki/Java_Persistence/OneToOne

 

 

본 웹사이트는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 웹사이트 서버의 유지 및 관리, 그리고 기술 콘텐츠 향상을 위해 쓰여집니다.
번호 제목 글쓴이 날짜 조회 수
38 Spring Batch 간단 정리 Sprong Batch 기초를 알아보자 file 졸리운_곰 2020.10.13 198
37 Spring Batch - ItemReader, ItemWriter, ItemProcessor 졸리운_곰 2020.10.13 370
36 spring batch» Spring Batch Tasklet Example file 졸리운_곰 2020.10.12 198
35 Spring Batch Example 2 - 간단한 Job만들기 file 졸리운_곰 2020.10.12 151
34 Spring batch Job 설정과 실행하기 file 졸리운_곰 2020.10.12 206
33 [Spring Batch] Configuring and Running a Job file 졸리운_곰 2020.10.12 121
32 [스프링] 스프링 배치 기본 개념 file 졸리운_곰 2019.12.24 244
31 Spring Batch 개념 정리 file 졸리운_곰 2019.12.24 257
30 스프링 배치 개발가이드 졸리운_곰 2019.12.24 321
29 Quartz + Spring Batch 조합! Quartz Scheduler 와 Spring Batch 의 궁합과 조합을 정리. file 졸리운_곰 2019.12.24 882
28 Spring Batch 간단 정리 file 졸리운_곰 2019.12.24 422
27 spring batch 소개 file 졸리운_곰 2019.12.24 208
26 Spring Batch - 소개 file 졸리운_곰 2019.12.24 207
25 Spring Batch Multithreading Example file 졸리운_곰 2019.01.31 285
24 Spring batch를 Parallel로 돌려보자 졸리운_곰 2019.01.31 399
23 Spring MVC 주요 애노테이션(Annotation)정리 file 졸리운_곰 2018.12.29 268
22 스프링/스프링부트 애노테이션(Annotation) 정리 졸리운_곰 2018.12.29 385
21 Spring Framework - annotation 정리 #1 졸리운_곰 2018.12.29 321
20 [Spring] Annotation 정리 졸리운_곰 2018.12.29 272
19 Maven 기초 사용법 졸리운_곰 2018.04.15 322
대표 김성준 주소 : 경기 용인 분당수지 U타워 등록번호 : 142-07-27414
통신판매업 신고 : 제2012-용인수지-0185호 출판업 신고 : 수지구청 제 123호 개인정보보호최고책임자 : 김성준 sjkim70@stechstar.com
대표전화 : 010-4589-2193 [fax] 02-6280-1294 COPYRIGHT(C) stechstar.com ALL RIGHTS RESERVED