简体   繁体   中英

JPA/MySql An eniity with incremental primary and secondary id

I have a resource ,say a Book. I want the book to have a book id and version id

On Create operations i want to have incremental id

book id     version id   status
1             0          ACTIVE
2             0          ACTIVE
3             0          ACTIVE

On update i want to have a new version for same id

book id     version id   status
    1             0          INACTIVE    //Changed to inactive
    1             1          ACTIVE      //new row with same id
    2             0          ACTIVE
    3             0          ACTIVE

i have two tables to achive this

A table to generate the id

  CREATE TABLE `BookIdGenerator` (
`id`int(11) NOT NULL AUTO_INCREMENT ,
PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

The Book table

 CREATE TABLE `Book` (
         `id` int(11) NOT NULL ,
         `version` int(11) NOT NULL,
         `title` varchar(255) NOT NULL,
         `isbn` varchar(255) NOT NULL,

     PRIMARY KEY (`id`,`version`),
     foreign key (`id`) references BookIdGenerator(`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

The entity class is

class Book{
private long id;
private int version;
//other fields

What would be the correct table structure and JPA annotations to achive this?

Without having any separate table I can have @Id at id and version but this does not allow to retrieve the assigned id to an object on persisting it.

update : I can have an Emeddable BookId class(with book id and version) and can use an @EmbeddedId in Book but I have to write a Id generator to generate the id. What can I do so that i don't have to write a generator to assign id before persisting in create method ?

JPA @Version

Your intended use of the version column is not as intended in JPA.

A version field is used to support the Optimistic Locking of individual DB records. Each time a transaction attempts to update a record, the version field is compared with the value in the DB. If they are the same then no other transaction has updated the record. The record will be updated and the version column changed. If they are not the same then some other transaction has updated the record and an OptimisticLockException will be thrown.

When updating entities (with JPA's merge()), JPA updates the existing record.

In your question, when you 'update' the book record, you are in fact creating(persisting) a new record.

Further, the version field is managed by JPA and should not be touched by the application – read only. In your example you are creating a new record with a specified version number which is not allowed.

Primary Keys

You have a PrimaryKey defined for Book, however, you require multiple records to hold the same PrimaryKey. This will fail as the PrimaryKey must be unique.

In this case your id and version form a Compound Primary Key, and you'll need to separate these fields out into a separate class; a Primary Key Class .

You would require an application specific versioning if you really want to record each change to a record as a separate record, as you can't use the JPA version field for your purposes.

Book_id     Book_Update_id   status    JPA_Version
    1             0          INACTIVE       0
    1             1          ACTIVE         0
    2             0          ACTIVE         0
    3             0          ACTIVE         0 

I've left a JPA version column in although this is not required if you are not modifying records.

so your Book will look something like this.

@Entity //Tell JPA this is an entity to be mapped to your DB.
@IdClass(BookId.class) //you need to repeat the PK fields in this class
class Book{
@Id @Column(name=”Book_id”) //Both id and bookUpdateId annotated with @ID
private long id;            //Together they for a compound Primary Key
@Id @Column(name=”Book_Update_id”)
String bookUpdateId
@Version @Column(name=”JPA_Version”) //Tell JPA where to record its version info
private int version;

It will be upto the application, when creating new records, to decide what the id and bookUpdateId values are to be based on the existing values of a record. There is no way JPA can know this. There is therefore no separate table required to support ID generation.

See Pro JPA 2 by Mike Keith and Merrick Schincariol for all these topics, a good introduction and beyond. This will aslo explain the @IdClass in more detail.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM