JPA: Java Persistence API,JAVA持久化API。

Entity:JPA 中的Entity是一种可持久化的域对象。一个Entity类对应关系数据库中的一张表,一个Entity实例对应关系数据库中的表的一行记录。

在这一个的表的设计当中,有时候会出现多字段作为共同主键的情况,这个时候在JPA里面有两种实现方式:

  1. 联合主键
  2. 嵌入式主键

联合主键

联合主键即主键声明在Entity之中,主键字段都声明上@Id, 并通过@IdClass 注解,引入主键字段的类。

Person.java

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
package com.proliu.multiprimary.entity;

import javax.persistence.*;

import lombok.*;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name="t_person")
@IdClass(PersonId.class)
public class Person {

@Id
private int id;

@Id
@Column(length=12)
private String name;

private int age;

@Version
private int version;

}

PersonId.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.proliu.multiprimary.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class PersonId implements Serializable {

private int id;

private String name;

}

@Getter@Setter@Builder@AllArgsConstructor@NoArgsConstructorlombok 工具提供的注解。

嵌入式主键

嵌入式主键即主键声明在@Embeddable声明的类之中,无需使用@Id, 并通过@EmbeddedId 注解引入主键所在的类,嵌入的类本身可以表示完整的主键值。

Animal.java

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
package com.proliu.multiprimary.entity;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Version;

import lombok.*;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name="t_animal")
public class Animal {

@EmbeddedId
private AnimalKey key;

private int age;

@Version
private int version;
}

AnimalKey.java

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
package com.proliu.multiprimary.entity;

import javax.persistence.Column;
import javax.persistence.Embeddable;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Embeddable
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class AnimalKey implements Serializable {

private int id;

@Column(length=12)
private String name;

}

都需要使用构造器,和复写对应的Equals HashCode方法,@Data 注解已经提供对应的方法。

测试插入数据

SpringBoot 启动类继承 CommandLineRunner实现启动后执行方法:

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.proliu.multiprimary;

import com.proliu.multiprimary.entity.Animal;
import com.proliu.multiprimary.entity.AnimalKey;
import com.proliu.multiprimary.entity.Person;
import com.proliu.multiprimary.entity.PersonId;
import com.proliu.multiprimary.repository.AnimalRepository;
import com.proliu.multiprimary.repository.PersonRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@Slf4j
@SpringBootApplication
public class MultiPrimaryApplication implements CommandLineRunner {

@Autowired
private PersonRepository personRepository;

@Autowired
private AnimalRepository animalRepository;

public static void main(String[] args) {
SpringApplication.run(MultiPrimaryApplication.class, args);
}

@Override
public void run(String... args) throws Exception {
//add record
Person person = Person.builder().id(1).name("test").age(12).build();
this.personRepository.save(person);

Animal animal = Animal.builder().key(AnimalKey.builder().id(1).name("test").build()).age(12).build();
this.animalRepository.save(animal);

//query
Person person1 = this.personRepository.findById(PersonId.builder().id(1).name("test").build()).get();
log.info("Get the person by id : {} , {}", 1, person1);

Animal animal1 = this.animalRepository.findById(AnimalKey.builder().id(1).name("test").build()).get();
log.info("Get the animal by id : {} , {}", 1, animal1);
}
}

启动类关键部分日志:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Hibernate: create table t_animal (id integer not null, name varchar(12) not null, age integer not null, version integer not null, primary key (id, name)) engine=InnoDB
Hibernate: create table t_person (id integer not null, name varchar(12) not null, age integer not null, version integer not null, primary key (id, name)) engine=InnoDB
2020-04-29 22:23:15.874 INFO 5785 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-04-29 22:23:15.882 INFO 5785 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-04-29 22:23:16.152 WARN 5785 --- [ main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-04-29 22:23:16.258 INFO 5785 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-29 22:23:16.445 INFO 5785 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2020-04-29 22:23:16.447 INFO 5785 --- [ main] c.p.m.MultiPrimaryApplication : Started MultiPrimaryApplication in 3.312 seconds (JVM running for 3.67)
Hibernate: select person0_.id as id1_1_0_, person0_.name as name2_1_0_, person0_.age as age3_1_0_, person0_.version as version4_1_0_ from t_person person0_ where person0_.id=? and person0_.name=?
Hibernate: insert into t_person (age, version, id, name) values (?, ?, ?, ?)
Hibernate: select animal0_.id as id1_0_0_, animal0_.name as name2_0_0_, animal0_.age as age3_0_0_, animal0_.version as version4_0_0_ from t_animal animal0_ where animal0_.id=? and animal0_.name=?
Hibernate: insert into t_animal (age, version, id, name) values (?, ?, ?, ?)
Hibernate: select person0_.id as id1_1_0_, person0_.name as name2_1_0_, person0_.age as age3_1_0_, person0_.version as version4_1_0_ from t_person person0_ where person0_.id=? and person0_.name=?
2020-04-29 22:23:16.520 INFO 5785 --- [ main] c.p.m.MultiPrimaryApplication : Get the person by id : 1 , com.proliu.multiprimary.entity.Person@7f642bf
Hibernate: select animal0_.id as id1_0_0_, animal0_.name as name2_0_0_, animal0_.age as age3_0_0_, animal0_.version as version4_0_0_ from t_animal animal0_ where animal0_.id=? and animal0_.name=?
2020-04-29 22:23:16.523 INFO 5785 --- [ main] c.p.m.MultiPrimaryApplication : Get the animal by id : 1 , com.proliu.multiprimary.entity.Animal@653a5967

代码

以上传代码到GitHub, 可以通过GitHub multi-primary-key 进行查看。