JPA annotation중에 PrimaryKey를 나타내는 @Id가 있다.

보통 getter쪽에 @Id라는 Annotaton을 매핑하면 그에 해당하는 맴버가 PrimaryKey가 된다.

 

하지만 복수의 컬럼이 하나의 Key로 나타내고자 할때는 @Id를 복수개 만큼 설정할수있을까?

내가 알고있는 만큼의 @Id의 사용은 Persistent 에서 유일하게 1개 만을 지정할 수 있다고 알고있다.

방법이 없을까 문서를 찾아보다가 JSR - 220 Enterprise JavaBeansTM 3.0

를 찾아보았다.

 

 

1. 복수의 키 실장을 위한 Annotation

 

@Embedded 와 @EmbeddedId

 

@EmbeddedId 의 설정 요건.

1. 일반 POJO다.

2. public none-arguments Constructor가 있어야 한다.

3. implements serializable

4. equals와 hashCode를 @Override해야한다.

 

@EmbeddedId 의 설정을 위해 위의 4가지 규칙을 준수해야만한다.

 

다음과 같은 Persistent POJO가 있다고 가정하면..

 

 

@Entity

@Table(name="auth")

public class Auth implements Serializable {

       @EmbeddedId

       private Auth.PK pk;

 

       @Column(name="au_auth")

       private Object auAuth;

 

       private static final long serialVersionUID = 1L;

..............................................>

 

 

복합키에 대한 값이 내부 Class로 정의 되어 있다.

 

Auth Inner Class

@Embeddable

       public static class PK implements Serializable {

             private String auMenu;

             private String mbId;

             private static final long serialVersionUID = 1L;

 

             public PK() {

                    super();

             }

 

             public String getAuMenu() {

                    return this.auMenu;

             }

 

             public void setAuMenu(String auMenu) {

                    this.auMenu = auMenu;

             }

 

             public String getMbId() {

                    return this.mbId;

             }

 

             public void setMbId(String mbId) {

                    this.mbId = mbId;

             }

 

             @Override

             public boolean equals(Object o) {

                    if (o == this) {

                           return true;

                    }

                    if ( ! (o instanceof PK)) {

                           return false;

                    }

                    PK other = (PK) o;

                    return this.auMenu.equals(other.auMenu)

                           && this.mbId.equals(other.mbId);

             }

 

             @Override

             public int hashCode() {

                    return this.auMenu.hashCode()

                           ^ this.mbId.hashCode();

             }

 

       }

 

equals와 HashCode를 override한다.

이것으로 복합키에 대한 Annotation 설정은 대략 정리 된다.

 

hashCode를 override하는 이유는 Object의 hashCode가 아닌 Serializable된 객체의 HashCode를 바라봄직하다.

 

기존에 @Id를 @Embeddable의 Type으로 본 POJO에 @EmbeddedId로써 등록을 했다.


출처 : http://blog.naver.com/paradozz?Redirect=Log&logNo=33908099







Tutorial JPA Composite Primary-Key

THERE ARE TWO NEW VERSIONS OF THIS POST.
SIMPLES COMPOSITE KEY: http://uaihebert.com/?p=1674&page=8
COMPLEX COMPOSITE KEY: http://uaihebert.com/?p=1674&page=9

Good afternoon.

Let us talk today about Composite Primary Key (Primary-Key) in a class?

What if only the ID it is not enough to define a unique record of your class? How can we make the JPA understand this situation of two fields as primary-key?

To help us to understand about Composite Key I will use a class named Car; this class has a car chassis serial number as primary key. Imagine that a new software requirement just arrive saying that, we need the Car primary key to be composed by the chassis serial number and with the engine serial number.

We will keep our JPA study at the same point that we have stopped in the last post (JPA SequenceGenerator). If you need any help to compile the code of this post, you can check the others posts about JPA that shows how to set up the environment: JPA TableGenerator – Simple Primay KeyAuto Create Schema Script with: Ant, Hibernate 3 e JPA 2Tutorial Hibernate 3 with JPA 2.

We need to create a class to be our composite primary-key.
Let us see how the CarPK code will be:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com;
 
import java.io.Serializable;
 
public class CarPK implements Serializable {
 
    private String chassisSerialNumber;
    private String engineSerialNumber;
 
    public CarPK(){
        // Your class must have a no-arq constructor
    }
 
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof CarPK){
            CarPK carPk = (CarPK) obj;
 
            if(!carPk.getChassisSerialNumber().equals(chassisSerialNumber)){
                return false;
            }
 
            if(!carPk.getEngineSerialNumber().equals(engineSerialNumber)){
                return false;
            }
 
            return true;
        }
 
        return false;
    }
 
    @Override
    public int hashCode() {
        return chassisSerialNumber.hashCode() + engineSerialNumber.hashCode();
    }
 
    public String getChassisSerialNumber() {
        return chassisSerialNumber;
    }
 
    public void setChassisSerialNumber(String chassisSerialNumber) {
        this.chassisSerialNumber = chassisSerialNumber;
    }
 
    public String getEngineSerialNumber() {
        return engineSerialNumber;
    }
 
    public void setEngineSerialNumber(String engineSerialNumber) {
        this.engineSerialNumber = engineSerialNumber;
    }
}

There are some rules that your PK class should follow:

  • It must have a default constructor without arguments.
  • It must implement the java.io.Serializable interface.
  • It must override the methods equals and hashCode.

Let us create now the Car class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.Table;
 
@Entity
@Table(name="CAR")
@IdClass(value=CarPK.class)
public class Car {
 
    @Id
    private String chassisSerialNumber;
    @Id
    private String engineSerialNumber;
 
    @Column
    private String name; // Yes, some people like to give name to theirs cars.
 
    public String getChassisSerialNumber() {
        return chassisSerialNumber;
    }
 
    public void setChassisSerialNumber(String chassisSerialNumber) {
        this.chassisSerialNumber = chassisSerialNumber;
    }
 
    public String getEngineSerialNumber() {
        return engineSerialNumber;
    }
 
    public void setEngineSerialNumber(String engineSerialNumber) {
        this.engineSerialNumber = engineSerialNumber;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}

Notice that in our Car class, we just added the @Id annotation without the need of any other annotation type. PS.: At the “name” attribute you will find the @Column annotation but this annotation is not necessary in our case.

Let us see our Main class code that will insert a record into the database:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
 
public class Main {
 
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();
 
        try {
            em.getTransaction().begin();
 
            Car car = new Car();
 
            car.setChassisSerialNumber("9BW DA05X6 1 T050136");
            car.setEngineSerialNumber("ABC123");
            car.setName("Thunder");
 
            em.persist(car);
 
            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }
 
        System.out.println("It is over");
    }
}

Run the Main class and check the result in the console.

Console Image.
Database Image.

How can we find our recorded entity that uses composite primary-key? Let us edit our Main class and see the result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com;
 
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
 
public class Main {
 
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();
 
        try {
            em.getTransaction().begin();
 
            CarPK carPK = new CarPK();
 
            carPK.setChassisSerialNumber("9BW DA05X6 1 T050136");
            carPK.setEngineSerialNumber("ABC123");
 
            Car car = em.find(Car.class, carPK);
 
            System.out.println(car.getName());
 
            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }
 
        System.out.println("It is over");
    }
}

Run the Main class again.

Console Image.

To find an entity that uses its ID as composite primary-key we need to create an instance of the PK class and use it as parameter in the “find” method.

There is another way to map this primary-key attribute. Notice that we have the same ID fields in both classes: “Car and CarPK“. It both has the chassisSerialNumber and the engineSerialNumber fields. With a little lines added/removed from our code we can refactor it to remove this duplicated fields.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com;
 
import java.io.Serializable;
 
import javax.persistence.Column;
import javax.persistence.Embeddable;
 
@Embeddable
public class CarPK implements Serializable {
 
    @Column
    private String chassisSerialNumber;
 
    @Column
    private String engineSerialNumber;
 
    public CarPK(){
        // Your class must have a no-arq constructor
    }
 
    @Override
    public boolean equals(Object obj) {
        if(!(obj instanceof CarPK)){
            CarPK carPk = (CarPK) obj;
 
            if(!carPk.getChassisSerialNumber().equals(chassisSerialNumber)){
                return false;
            }
 
            if(!carPk.getEngineSerialNumber().equals(engineSerialNumber)){
                return false;
            }
 
            return true;
        }
 
        return false;
    }
 
    @Override
    public int hashCode() {
        return chassisSerialNumber.hashCode() + engineSerialNumber.hashCode();
    }
 
    public String getChassisSerialNumber() {
        return chassisSerialNumber;
    }
 
    public void setChassisSerialNumber(String chassisSerialNumber) {
        this.chassisSerialNumber = chassisSerialNumber;
    }
 
    public String getEngineSerialNumber() {
        return engineSerialNumber;
    }
 
    public void setEngineSerialNumber(String engineSerialNumber) {
        this.engineSerialNumber = engineSerialNumber;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com;
 
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
 
@Entity
@Table(name = "CAR")
public class Car {
 
    @EmbeddedId
    private CarPK carPK;
 
    @Column
    private String name; // Yes, some people like to give name to theirs cars.
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public CarPK getCarPK() {
        return carPK;
    }
 
    public void setCarPK(CarPK carPK) {
        this.carPK = carPK;
    }
}

We can see that just a few changes were done in the classes:

  • In the CarPK class there is a new annotation: the @Column. The car ID attributes will be mapped only inside the CarPK. There is a new annotation named @Embeddeable allowing our class to be part of another class.
  • We removed the Id attributes from the Car class. We also removed the @IdClass annotation. We added an attribute of the CarPK with the @EmbeddedId, this annotation allow the Car to use the CarPK fields as its primary-key.

If you wish, run the main class again and you will see that our class is returned by the find method without errors.

WarningWhen your class implements the composite primary-key it will not be able to use the sequence id generation. You will have to generate the ID by your software code.

Do you have any doubts or suggestions? Just post it.

See you later.


출처 - http://uaihebert.com/?p=42





I have a problem with JPA mapping for a particular relation. In my MySQL database I have 3 entity in relationship:

USER
USER_SERIAL_NUMBER VARCHAR(20) not null,
..... VARCHAR(4) not null,
..... VARCHAR(3) not null,
..... VARCHAR(10) not null,
primary key (USER_SERIAL_NUMBER)
);
OFFICE (
OFFICE_SERIAL_NUMBER VARCHAR(20) not null,
TOWN VARCHAR(15) not null,
YEAR VARCHAR(4) not null,
DESCRIPTION VARCHAR(200) not null,
NOTE VARCHAR(100) not null,
CREATION_DATE DATE not null,
LAST_UPDATE_DATE DATE not null,
primary key (OFFICE_SERIAL_NUMBER)
);

ROLE_TYPE (
ROLE_ID BIGINT not null,
DESCRIPTION VARCHAR(20) not null,
CREATION_DATE DATE not null,
LAST_UPDATE_DATE DATE not null,
primary key (ROLE_ID)
);

and the relation table

ROLE (
ROLE_ID BIGINT not null,
USER_SERIAL_NUMBER VARCHAR(20) not null,
OFFICE_SERIAL_NUMBER VARCHAR(20) not null,
YEAR VARCHAR(4) not null,
CREATION_DATE DATE not null,
LAST_UPDATE_DATE DATE not null,
primary key (ROLE_ID, USER_SERIAL_NUMBER, OFFICE_SERIAL_NUMBER, YEAR)
);

In the relation table I have a composite key with the 3 foreign key and a local key (the year), because a user can have more (different) role on particular office in a specified year but probably will change the next year (I have to mantain the historic role).

I have made many attempts to map these tables in entities but I didn't yet found the right mix so that everything works.

I would like to navigate the relationship in both direction, so I need to know who and how many are users who have a particular role in a particular office, but I also need to know in how many offices work a particular user, and what role he has.

Here is the Java code:

@Entity
@Scope(ScopeType.CONVERSATION)
@Name("role")
@Table(name = "ROLE")
@IdClass(RolePK.class)
public class Role implements Serializable {

private static final long serialVersionUID = 111111111L;

@Id
@OneToOne
@Column(name="USER_SERIAL_NUMBER")
private User User;

@Id
@OneToOne
@Column(name="OFFICE_SERIAL_NUMBER")
private Office office;

@Id
@OneToOne
@Column(name="ROLE_ID")
private RoleType roleType;

@Id
@Column(name = "YEAR", length = 4, nullable = false)
private String year;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CREATION_DATE", nullable = false)
private Date creationDate;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "LAST_UPDATE_DATE", nullable = false)
private Date lastUpdateDate;

 getter and setter  
 equals and hash code 
 toString 
}

@Name("rolePK")
public class RolePK implements Serializable {

private static final long serialVersionUID = 2222222222222L;

private String office;
private Long roleType;
private String User;
private String year;

public RolePK() {

 getter and setter  
 equals and hash code 
 toString 
}
@Entity
@Scope(ScopeType.CONVERSATION)
@Name("user")
@Table(name = "USER")
public class User implements Serializable {

private static final long serialVersionUID = 333333333333L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "USER_SERIAL_NUMBER", length = 20, nullable = false)
private String serialNumber;

 other properties 

@OneToMany(mappedBy="user")
private Collection<Role> roleCollection;

 getter and setter  
 equals and hash code 
 toString 


@Entity
@Scope(ScopeType.CONVERSATION)
@Name("office")
@Table(name = "OFFICE")
public class Office implements Serializable {

private static final long serialVersionUID = 55555555555555555L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name="OFFICE_SERIAL_NUMBER", length=20, nullable=false)
private String officeSerialNumber;
 other properties 

@OneToMany(mappedBy="office")
private Collection<Role> roleCollection;

 getter and setter  
 equals and hash code 
 toString 

@Entity
@Scope(ScopeType.CONVERSATION)
@Name("roleType")
@Table(name = "ROLE_TYPE")
public class RoleType implements Serializable {

private static final long serialVersionUID = 44444444444444444L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "ROLE_TYPE_ID", nullable = false)
private Long id; 
 other properties 

@OneToMany(mappedBy="roleType")
private Collection<Role> roleCollection;

 getter and setter  
 equals and hash code 
 toString 

I tried to implement this solution (which I had already tried before), but I still have configuration problems. In this particular configuration, the error I have is that when Hibernate tries to create a query, does not seem to be able to generate a proper query. In particular, I have the names of the properties that are not equal to the names of the columns of the relationship table. I tried to use annotations to specify the name of the column on which to bind the property, but the query is generated using the property name and not the name of the column. Thus, the system throws an exception, specifically: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column 'roleco0_.user'in 'field list'.

Someone can help me? Thanks in advance

share|improve this question

Why don't you introduce an autogenerated ID in the Role table, and a unique constraint on the tuple (roleId, use, office, year). This would make sthings much much simpler.

If you really want to continue with this composite primary key, then you should readhttp://docs.jboss.org/hibernate/core/3.6/reference/en-US/html_single/#mapping-declaration-id, which ends with an example of an embedded ID containing an association. But be careful with your associations : they should be ManyToOne, and not OneToOne, since a user (for example) can be referenced by several roles.

You should thus have

@Embeddable
public class RoleId {
    @ManyToOne
    private User user;

    @ManyToOne
    private Office office;

    @ManyToOne
    private RoleType roleType;

    private int year;

    // constructor, getters, equals, hashCode
}

@Entity
public class Role {
    @EmbeddedId
    private RoleId id;

    private Date creationDate;
    // ...
}



출처 - http://stackoverflow.com/questions/5258630/jpa-mapping-composite-key-in-relationship





Posted by linuxism
,