추가 열이있는 다 대다 연관 테이블 맵핑


130

내 데이터베이스에는 3 개의 테이블이 있습니다. 사용자 및 서비스 엔터티는 다 대다 관계를 가지며 다음과 같이 SERVICE_USER 테이블과 조인됩니다.

사용자-SERVICE_USER-서비스

SERVICE_USER 테이블에는 추가 BLOCKED 열이 있습니다.

그러한 매핑을 수행하는 가장 좋은 방법은 무엇입니까? 이들은 내 엔터티 클래스입니다

@Entity
@Table(name = "USERS")
public class User implements java.io.Serializable {

private String userid;
private String email;

@Id
@Column(name = "USERID", unique = true, nullable = false,)
public String getUserid() {
return this.userid;
}

.... some get/set methods
}

@Entity
@Table(name = "SERVICES")
public class CmsService implements java.io.Serializable {
private String serviceCode;

@Id
@Column(name = "SERVICE_CODE", unique = true, nullable = false, length = 100)
public String getServiceCode() {
return this.serviceCode;
}
.... some additional fields and get/set methods
}

나는이 예제 http://giannigar.wordpress.com/2009/09/04/m ... using-jpa /를 따랐다. 다음은 테스트 코드이다.

User user = new User();
user.setEmail("e2");
user.setUserid("ui2");
user.setPassword("p2");

CmsService service= new CmsService("cd2","name2");

List<UserService> userServiceList = new ArrayList<UserService>();

UserService userService = new UserService();
userService.setService(service);
userService.setUser(user);
userService.setBlocked(true);
service.getUserServices().add(userService);

userDAO.save(user);

문제는 최대 절전 모드가 User 객체와 UserService를 유지한다는 것입니다. CmsService 개체에 성공하지 못함

EAGER 가져 오기를 사용하려고했습니다-진행하지 않습니다

위에 제공된 매핑으로 예상되는 동작을 달성 할 수 있습니까?

추가 열이있는 많은 조인 테이블을 여러 테이블에 매핑하는 더 우아한 방법이 있습니까?

답변:


192

SERVICE_USER 테이블은 순수한 조인 테이블이 아니지만 추가 기능 필드 (차단)가 있으므로이를 엔티티로 맵핑하고 사용자와 서비스 사이의 다 대다 연관을 두 개의 OneToMany 연관으로 분해해야합니다. 하나의 사용자는 많은 UserServices, 하나의 서비스에는 많은 UserService가 있습니다.

당신은 우리에게 가장 중요한 부분을 보여주지 않았습니다 : 당신의 엔티티들 사이의 관계의 매핑과 초기화 (즉, 당신이 문제가있는 부분). 어떻게 생겼는지 보여 드리겠습니다.

관계를 양방향으로 만들면

class User {
    @OneToMany(mappedBy = "user")
    private Set<UserService> userServices = new HashSet<UserService>();
}

class UserService {
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne
    @JoinColumn(name = "service_code")
    private Service service;

    @Column(name = "blocked")
    private boolean blocked;
}

class Service {
    @OneToMany(mappedBy = "service")
    private Set<UserService> userServices = new HashSet<UserService>();
}

관계에 계단식을 배치하지 않으면 모든 엔티티를 유지 / 저장해야합니다. 관계의 소유 측 (여기서는 UserService 측) 만 초기화해야하지만 양쪽이 일관성을 유지하는 것이 좋습니다.

User user = new User();
Service service = new Service();
UserService userService = new UserService();

user.addUserService(userService);
userService.setUser(user);

service.addUserService(userService);
userService.setService(service);

session.save(user);
session.save(service);
session.save(userService);

2
단지 추가하는 것입니다. 이것이 제 의견으로는 최선의 방법이지만 (저는 항상 성능상의 이유로 FK를 소유 한 것을 엔터티로 매핑하는 것을 선호합니다) 실제로 유일한 방법은 아닙니다. SERVICE_USER 테이블의 값을 컴포넌트 (JPA가 임베드 가능 파일이라고 함)로 맵핑 @ElementCollection하고 사용자 및 서비스 엔티티 중 하나 (또는 ​​둘 다)를 사용할 수도 있습니다 .
Steve Ebersole

6
UserService 테이블의 기본 키는 어떻습니까? 사용자 및 서비스 외래 키의 조합이어야합니다. 매핑 되었습니까?
Jonas Gröger

24
나는 그렇게하지 않을 것입니다. 복합 키는 고통스럽고 비효율적이며 최대 절전 모드는 복합 키를 사용하지 않는 것이 좋습니다. 다른 엔티티와 마찬가지로 자동 생성 된 ID를 사용하면 수명이 훨씬 간단 해집니다. 의 단일성을 보장하려면 [userFK, serviceFK]고유 제한 조건을 사용하십시오.
JB 니 제트

1
@GaryKephart : 자신의 코드와 자신의 매핑으로 자신의 질문을하십시오.
JB 니 제트

1
@gstackoverflow : 최대 절전 모드 4는 이와 관련하여 아무것도 변경하지 않습니다. 나는 그것이 어떻게 우아하지 않은지 알지 못한다.
JB 니 제트

5

XML 파일 구성에서 최대 절전 모드가있는 추가 열이있는 다 대다 연결 테이블을 매핑하는 방법을 검색했습니다.

두 개의 테이블 'a'& 'c'가 있고 'extra'라는 열과 다 대다 연관되어 있다고 가정합니다. 완전한 예제를 찾지 못했습니다. 내 코드는 다음과 같습니다. 그것이 도움이되기를 바랍니다 :).

우선 여기에 Java 객체가 있습니다.

public class A implements Serializable{  

    protected int id;
    // put some others fields if needed ...   
    private Set<AC> ac = new HashSet<AC>();

    public A(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public Set<AC> getAC() {
        return ac;
    }

    public void setAC(Set<AC> ac) {
        this.ac = ac;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        final int prime = 97;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof A))
            return false;
        final A other = (A) obj;
        if (id != other.getId())
            return false;
        return true;
    }

}

public class C implements Serializable{

    protected int id;
    // put some others fields if needed ...    

    public C(int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        final int prime = 98;
        int result = 1;
        result = prime * result + id;
        return result;
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof C))
            return false;
        final C other = (C) obj;
        if (id != other.getId())
            return false;
        return true;
    }

}

이제 연결 테이블을 만들어야합니다. 첫 번째 단계는 복잡한 기본 키 (a.id, c.id)를 나타내는 객체를 만드는 것입니다.

public class ACId implements Serializable{

    private A a;
    private C c;

    public ACId() {
        super();
    }

    public A getA() {
        return a;
    }
    public void setA(A a) {
        this.a = a;
    }
    public C getC() {
        return c;
    }
    public void setC(C c) {
        this.c = c;
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((a == null) ? 0 : a.hashCode());
        result = prime * result
                + ((c == null) ? 0 : c.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        ACId other = (ACId) obj;
        if (a == null) {
            if (other.a != null)
                return false;
        } else if (!a.equals(other.a))
            return false;
        if (c == null) {
            if (other.c != null)
                return false;
        } else if (!c.equals(other.c))
            return false;
        return true;
    }
}

이제 연결 개체 자체를 만들어 봅시다.

public class AC implements java.io.Serializable{

    private ACId id = new ACId();
    private String extra;

    public AC(){

    }

    public ACId getId() {
        return id;
    }

    public void setId(ACId id) {
        this.id = id;
    }

    public A getA(){
        return getId().getA();
    }

    public C getC(){
        return getId().getC();
    }

    public void setC(C C){
        getId().setC(C);
    }

    public void setA(A A){
        getId().setA(A);
    }

    public String getExtra() {
        return extra;
    }

    public void setExtra(String extra) {
        this.extra = extra;
    }

    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        AC that = (AC) o;

        if (getId() != null ? !getId().equals(that.getId())
                : that.getId() != null)
            return false;

        return true;
    }

    public int hashCode() {
        return (getId() != null ? getId().hashCode() : 0);
    }
}

이제 모든 클래스를 최대 절전 XML 구성으로 매핑 할 차례입니다.

A.hbm.xml과 C.hxml.xml (동일하게 동일).

<class name="A" table="a">
        <id name="id" column="id_a" unsaved-value="0">
            <generator class="identity">
                <param name="sequence">a_id_seq</param>
            </generator>
        </id>
<!-- here you should map all others table columns -->
<!-- <property name="otherprop" column="otherprop" type="string" access="field" /> -->
    <set name="ac" table="a_c" lazy="true" access="field" fetch="select" cascade="all">
        <key>
            <column name="id_a" not-null="true" />
        </key>
        <one-to-many class="AC" />
    </set>
</class>

<class name="C" table="c">
        <id name="id" column="id_c" unsaved-value="0">
            <generator class="identity">
                <param name="sequence">c_id_seq</param>
            </generator>
        </id>
</class>

그런 다음 연관 맵핑 파일 a_c.hbm.xml.

<class name="AC" table="a_c">
    <composite-id name="id" class="ACId">
        <key-many-to-one name="a" class="A" column="id_a" />
        <key-many-to-one name="c" class="C" column="id_c" />
    </composite-id>
    <property name="extra" type="string" column="extra" />
</class>

테스트 할 코드 샘플은 다음과 같습니다.

A = ADao.get(1);
C = CDao.get(1);

if(A != null && C != null){
    boolean exists = false;
            // just check if it's updated or not
    for(AC a : a.getAC()){
        if(a.getC().equals(c)){
            // update field
            a.setExtra("extra updated");
            exists = true;
            break;
        }
    }

    // add 
    if(!exists){
        ACId idAC = new ACId();
        idAC.setA(a);
        idAC.setC(c);

        AC AC = new AC();
        AC.setId(idAC);
        AC.setExtra("extra added"); 
        a.getAC().add(AC);
    }

    ADao.save(A);
}

2

앞에서 언급했듯이 JPA에서는 추가 열을 사용하려면 단일 ManyToMany 관계 대신 두 개의 OneToMany 연관을 사용해야합니다. 자동 생성 된 값으로 열을 추가 할 수도 있습니다. 이런 식으로 유용한 경우 테이블의 기본 키로 작동 할 수 있습니다.

예를 들어, 추가 클래스의 구현 코드는 다음과 같아야합니다.

@Entity
@Table(name = "USER_SERVICES")
public class UserService{

    // example of auto-generated ID
    @Id
    @Column(name = "USER_SERVICES_ID", nullable = false)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long userServiceID;



    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "USER_ID")
    private User user;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "SERVICE_ID")
    private Service service;



    // example of extra column
    @Column(name="VISIBILITY")    
    private boolean visibility;



    public long getUserServiceID() {
        return userServiceID;
    }


    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Service getService() {
        return service;
    }

    public void setService(Service service) {
        this.service = service;
    }

    public boolean getVisibility() {
        return visibility;
    }

    public void setVisibility(boolean visibility) {
        this.visibility = visibility;
    }

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.