111

I want to have versions from the same data entry. In other words, I want to duplicate the entry with another version number.

id - Version will be the primary key.

How should the entity look like? How can I duplicate it with another version?

id Version ColumnA

1   0      Some data
1   1      Some Other data
2   0      Data 2. Entry
2   1      Data
reformed
  • 3,922
  • 9
  • 51
  • 77
Kayser
  • 6,116
  • 17
  • 49
  • 86
  • When using the `@IdClass` annotation, another tip I found is the `@Column` annotation should go into the Entity class' fields (`YourEntity` in RohitJan's sample code). – KenSV Nov 30 '17 at 03:39

4 Answers4

241

You can make an Embedded class, which contains your two keys, and then have a reference to that class as EmbeddedId in your Entity.

You would need the @EmbeddedId and @Embeddable annotations.

@Entity
public class YourEntity {
    @EmbeddedId
    private MyKey myKey;

    @Column(name = "ColumnA")
    private String columnA;

    /** Your getters and setters **/
}
@Embeddable
public class MyKey implements Serializable {

    @Column(name = "Id", nullable = false)
    private int id;

    @Column(name = "Version", nullable = false)
    private int version;

    /** getters and setters **/
}

Another way to achieve this task is to use @IdClass annotation, and place both your id in that IdClass. Now you can use normal @Id annotation on both the attributes

@Entity
@IdClass(MyKey.class)
public class YourEntity {
   @Id
   private int id;
   @Id
   private int version;

}

public class MyKey implements Serializable {
   private int id;
   private int version;
}
pixel
  • 21,352
  • 30
  • 113
  • 196
Rohit Jain
  • 195,192
  • 43
  • 369
  • 489
  • 7
    Is it possible to use `@Generatedvalue` for Id's by EmbeddedId – Kayser Oct 24 '12 at 08:15
  • 1
    @Kayser. As far as I know. No. You have to explicitly set the value for them in your KeyClass instance, and then set that key class instance in your Entity. – Rohit Jain Oct 24 '12 at 08:16
  • 1
    @Kayser. `@GeneratedValue` can only be used to generate key values for a primary key, it cannot generate combination for composite keys. – Rohit Jain Oct 24 '12 at 08:17
  • 1
    @Kayser. See `Section - 11.1.17 GeneratedValue Annotation` of JPA 2.0 Specification. It clearly says that, `@GeneratedValue` can only be used with simple primary key. – Rohit Jain Oct 24 '12 at 08:20
  • 1
    @RohitJain just one thing: you actually cannot make embedded class public (needs to be in its own file to be public) – Lucas Mar 31 '14 at 10:15
  • +1 Nice answer, would be even better if you could link to a doc where this approach is described – avalancha Aug 01 '14 at 12:58
  • Only to add information: `MyKey` class must be static to work with JPA! – andPat Nov 11 '15 at 11:03
  • Is it possible to say to JPA (in eclise from new "jpa entity from table") to generate a single class file instead of two bean, one with data and other with primary key? @RohitJain – Accollativo Mar 15 '16 at 10:59
  • Just to add: if you have for example a "MyEntity" class of a table in which every column is a primary key, you can declare it `@Embeddable` and use the annotation `@IdClass(value=MyEntity.class)` (yes, the IdClass is the "MyEntity" class itself) and annotate all the primary key columns with `@Id`. If you happen to use Spring Data JPA, your repository will have to extend `CrudRepository`. – nonzaprej Apr 03 '17 at 15:43
  • Sorry @AbdullahKhan- site seems to be removed or down. Will leave there for now in case it comes back up – Fast Engy Aug 08 '17 at 07:04
  • 1
    @FastEngy It can still be accessed via Wayback Machine: https://web.archive.org/web/20170123035517/http://uaihebert.com/tutorial-jpa-composite-primary-key. It seems that this article is superseded by https://web.archive.org/web/20170202203555/http://uaihebert.com/jpa-mini-book-first-steps-and-detailed-concepts/8/ and https://web.archive.org/web/20161014051056/http://uaihebert.com/jpa-mini-book-first-steps-and-detailed-concepts/9/ which also vanished… – radlan Mar 05 '18 at 15:54
  • For the 'Another way to achieve this task...' (for me) the `MyKey` class with annotation `@IdClass(MyKey.class)` the class had to be `static` in order to work. – Dirk Schumacher Dec 15 '19 at 11:17
10

The MyKey class must implement Serializable if you are using @IdClass

Bö macht Blau
  • 11,639
  • 4
  • 30
  • 53
Swapnil17
  • 526
  • 1
  • 5
  • 20
5

Key class:

@Embeddable
@Access (AccessType.FIELD)
public class EntryKey implements Serializable {

    public EntryKey() {
    }

    public EntryKey(final Long id, final Long version) {
        this.id = id;
        this.version = version;
    }

    public Long getId() {
        return this.id;
    }

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

    public Long getVersion() {
        return this.version;
    }

    public void setVersion(Long version) {
        this.version = version;
    }

    public boolean equals(Object other) {
        if (this == other)
            return true;
        if (!(other instanceof EntryKey))
            return false;
        EntryKey castOther = (EntryKey) other;
        return id.equals(castOther.id) && version.equals(castOther.version);
    }

    public int hashCode() {
        final int prime = 31;
        int hash = 17;
        hash = hash * prime + this.id.hashCode();
        hash = hash * prime + this.version.hashCode();
        return hash;
    }

    @Column (name = "ID")
    private Long id;
    @Column (name = "VERSION")
    private Long operatorId;
}

Entity class:

@Entity
@Table (name = "YOUR_TABLE_NAME")
public class Entry implements Serializable {

    @EmbeddedId
    public EntryKey getKey() {
        return this.key;
    }

    public void setKey(EntryKey id) {
        this.id = id;
    }

    ...

    private EntryKey key;
    ...
}

How can I duplicate it with another Version?

You can detach entity which retrieved from provider, change the key of Entry and then persist it as a new entity.

Victor Stafusa
  • 12,608
  • 11
  • 54
  • 67
  • Ist it possible to define the id in Entrykey `AUTOGENERATED`. oder something like that `@GeneratedValue(strategy = GenerationType.IDENTITY)` – Kayser Oct 23 '12 at 15:35
  • 1
    I'm also wondering how to calculate hash for 2 long primary keys. As for `hash` and `prime` in method `hashCode` in class `EntryKey`, can you tell me where that idea comes from? – Bruce Sun Mar 26 '17 at 02:33
2

The MyKey class (@Embeddable) should not have any relationships like @ManyToOne

Ranuka
  • 643
  • 7
  • 11
  • Why not? Have you looked at this [example](https://vladmihalcea.com/the-best-way-to-map-a-composite-primary-key-with-jpa-and-hibernate/)? – Buhake Sindi Jun 29 '18 at 19:11