java - 이란 - 자바 스윙 튜토리얼




합성 기본 키를 만드는 방법(Java 지속성 주석) (4)

테이블 user_roles가 두 개의 열 (userID, roleID)을 복합 기본 키로 정의하도록 만드는 방법. 쉽게, 기억할 수 없다 / 찾을 수 있어야합니다.

user 엔티티에서 :

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<RoleDAO> getRoles() {
    return roles;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getUserID() {
    return userID;
}

roles 엔티티에서 :

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<UserDAO> getUsers() {
    return users;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getRoleID() {
    return roleID;
}

고맙습니다.

** 더 많은 정보

따라서 세 번째 테이블 인 user_roles (위에서 생성 된 자동)는 user 엔티티의 userIDroles 엔티티의 roleID 를 사용 roles . 이제 생성 된 테이블 ( user_roles )의 두 개의 열이 복합 기본 키가되어야합니다.


기본 키와 동일한 문제가있었습니다. 또한 @Embeddable 및 @EmbeddedId 클래스를 사용하여 솔루션을 알았습니다. 하지만 주석을 사용하여 간단한 솔루션을 원했습니다.

글쎄, 나는이 기사를 통해 깨달음을 발견했다 : http://www.vaannila.com/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

여기에 마술이 있습니다.

그러면 조인 테이블에 기본 키가 생성됩니다.

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="classA_classB")
private Set<ClassA> classesA;

이것은 조인 테이블에 기본 키를 생성하지 않습니다.

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name="classA_classB")
private List<ClassA> classesA;

적어도 내 환경에서는

차이점은 Set 또는 List를 사용한다는 것입니다.


당신은 이미 당신이 묻는대로 정확하게하는 몇 가지 좋은 해답을 여기에 가지고 있습니다.

참고로, 대용 키를 프라이 머리 키로 사용하고 비즈니스 키를 NaturalId로 표시하는 것입니다.

대리 키를 기본 키로 사용하는 것이 좋지만 모든 엔터티의 자연 키를 식별해야합니다. 자연 키는 고유하고 null이 아닌 속성 또는 속성의 조합입니다. 또한 불변입니다. 요소 내부의 자연 키의 속성을 매핑합니다. Hibernate는 필요한 유일 키와 null 허용 제한을 생성 할 것이고, 결과적으로 당신의 매핑은 더 문서화가 될 것이다.

엔티티의 자연 키 특성을 비교하려면 equals () 및 hashCode ()를 구현하는 것이 좋습니다.

코드에서 주석을 사용하면 다음과 같이 표시됩니다.

@Entity
public class UserRole {
  @Id
  @GeneratedValue
  private long id;

  @NaturalId
  private User user;
  @NaturalId
  private Role role;
}

이 기능을 사용하면 복잡한 기본 키를 자주 참조 / 매핑해야 할 때 알 수 있으므로 많은 어려움을 덜어줍니다.

나는 이것을 힘든 방법으로 발견했고, 결국에는 최대 절전 모드와의 싸움을 포기하고 대신 그 흐름을 따라 가기로 결심했다. 기존 소프트웨어 또는 종속성을 다루고 있기 때문에 이것이 가능하지 않을 수도 있음을 충분히 이해합니다. 그러나 나중에 참조 할 수 있도록 언급하고 싶습니다. ( 다른 사람이 사용하지 못할 수도 있습니다 !)


요구 사항을 충족시키기 위해 @ManyToMany를 @OneToMany 매핑으로 매핑 할 수 있습니다. 이렇게하면 USER_ROLE에 복합 기본 키로 USER_ID와 ROLE_ID가 모두 포함됩니다.

나는 당신에게 어떻게 보여줄 것인가?

@Entity
@Table(name="USER")
public class User {

    @Id
    @GeneratedValue
    private Integer id;

    @OneToMany(cascade=CascadeType.ALL, mappedBy="joinedUserRoleId.user")
    private List<JoinedUserRole> joinedUserRoleList = new ArrayList<JoinedUserRole>();

    // no-arg required constructor
    public User() {}

    public User(Integer id) {
        this.id = id;
    }

    // addRole sets up bidirectional relationship
    public void addRole(Role role) {
        // Notice a JoinedUserRole object
        JoinedUserRole joinedUserRole = new JoinedUserRole(new JoinedUserRole.JoinedUserRoleId(this, role));

        joinedUserRole.setUser(this);
        joinedUserRole.setRole(role);

        joinedUserRoleList.add(joinedUserRole);
    }

}

@Entity
@Table(name="USER_ROLE")
public class JoinedUserRole {

    public JoinedUserRole() {}

    public JoinedUserRole(JoinedUserRoleId joinedUserRoleId) {
        this.joinedUserRoleId = joinedUserRoleId;
    }

    @ManyToOne
    @JoinColumn(name="USER_ID", insertable=false, updatable=false)
    private User user;

    @ManyToOne
    @JoinColumn(name="ROLE_ID", insertable=false, updatable=false)
    private Role role;

    @EmbeddedId
    // Implemented as static class - see bellow
    private JoinedUserRoleId joinedUserRoleId;

    // required because JoinedUserRole contains composite id
    @Embeddable
    public static class JoinedUserRoleId implements Serializable {

        @ManyToOne
        @JoinColumn(name="USER_ID")
        private User user;

        @ManyToOne
        @JoinColumn(name="ROLE_ID")
        private Role role;

        // required no arg constructor
        public JoinedUserRoleId() {}

        public JoinedUserRoleId(User user, Role role) {
            this.user = user;
            this.role = role;
        }

        public JoinedUserRoleId(Integer userId, Integer roleId) {
            this(new User(userId), new Role(roleId));
        }

        @Override
        public boolean equals(Object instance) {
            if (instance == null)
                return false;

            if (!(instance instanceof JoinedUserRoleId))
                return false;

            final JoinedUserRoleId other = (JoinedUserRoleId) instance;
            if (!(user.getId().equals(other.getUser().getId())))
                return false;

            if (!(role.getId().equals(other.getRole().getId())))
                return false;

            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 47 * hash + (this.user != null ? this.user.hashCode() : 0);
            hash = 47 * hash + (this.role != null ? this.role.hashCode() : 0);
            return hash;
        }

    }

}

생각해 내다

객체에 할당 된 식별자 또는 복합 키가있는 경우 식별자는 save ()를 호출하기 전에 객체 인스턴스에 할당해야합니다 (SHOULD).

그래서 우리는 이것을 처리하기 위해 JoinedUserRoleId 생성자를 생성했습니다.

public JoinedUserRoleId(User user, Role role) {
    this.user = user;
    this.role = role;
}

마지막으로 Role 클래스

@Entity
@Table(name="ROLE")
public class Role {

    @Id
    @GeneratedValue
    private Integer id;

    @OneToMany(cascade=CascadeType.ALL, mappedBy="JoinedUserRoleId.role")
    private List<JoinedUserRole> joinedUserRoleList = new ArrayList<JoinedUserRole>();

    // no-arg required constructor
    public Role() {}

    public Role(Integer id) {
        this.id = id;
    }

    // addUser sets up bidirectional relationship
    public void addUser(User user) {
        // Notice a JoinedUserRole object
        JoinedUserRole joinedUserRole = new JoinedUserRole(new JoinedUserRole.JoinedUserRoleId(user, this));

        joinedUserRole.setUser(user);
        joinedUserRole.setRole(this);

        joinedUserRoleList.add(joinedUserRole);
    }

}

테스트에 따르면 다음과 같이 씁니다.

User user = new User();
Role role = new Role();

// code in order to save a User and a Role
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();

Serializable userId  = session.save(user);
Serializable roleId = session.save(role);

session.getTransaction().commit();
session.clear();
session.close();

// code in order to set up bidirectional relationship
Session anotherSession = HibernateUtil.getSessionFactory().openSession();
anotherSession.beginTransaction();

User savedUser = (User) anotherSession.load(User.class, userId);
Role savedRole = (Role) anotherSession.load(Role.class, roleId);

// Automatic dirty checking
// It will set up bidirectional relationship
savedUser.addRole(savedRole);

anotherSession.getTransaction().commit();
anotherSession.clear();
anotherSession.close();

위의 코드에 따라 JoinedUserRole 클래스에 대한 참조가 없습니다.

JoinedUserRole을 검색하려면 다음을 시도하십시오.

Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();

Integer userId;
Integer roleId;

// Lets say you have set up both userId and roleId
JoinedUserRole joinedUserRole = (JoinedUserRole) session.get(JoinedUserRole.class, new JoinedUserRole.JoinedUserRoleId(userId, roleId));

// some operations

session.getTransaction().commit();
session.clear();
session.close();

문안 인사,


질문을 개선하고 제안을 고려해 주셔서 감사합니다.

(미안하지만, 당신이 엔티티를 Daos로 후위 접하는 것이 약간 이상하지만, 그다지 중요하지 않습니다.)

문제가 남아 있는지 확실하지 않습니다.

  • 당신이 기술하는 두 엔티티는 각각 한 쌍의 PK가 아니라 하나의 PK를 가지고 있습니다.
  • 링크 테이블에는 해당 엔티티가 없으며 두 엔티티와 ManyToMany 관계에 의해 암시 적으로 정의됩니다. 세 번째 엔티티가 필요하면 ManyToMany를 OneToMany와 ManyToOne 관계 쌍으로 변경하십시오.




many-to-many