Spring JPA:如何使用两个外键处理实体映射,这两个外键一起充当主键
我有两张桌子: 表ASpring JPA:如何使用两个外键处理实体映射,这两个外键一起充当主键,spring,hibernate,spring-boot,jpa,Spring,Hibernate,Spring Boot,Jpa,我有两张桌子: 表A id(PK) name ------------ 1 aa 2 ab 3 ac 4 ad 表B master_id(FK_id) slave_id(FK_id) 1 2 2 3 2 4 表A和B具有一对多关系。 如上图所示,表B有两列的复合键(master_id,slave_id)。表B中的两列都与表A有外键
id(PK) name
------------
1 aa
2 ab
3 ac
4 ad
表B
master_id(FK_id) slave_id(FK_id)
1 2
2 3
2 4
表A和B具有一对多关系。
如上图所示,表B有两列的复合键(master_id,slave_id)。表B中的两列都与表A有外键关系
我们如何使用spring JPA/hibernate处理此实体映射?A表的映射非常简单:
@Entity
public class A {
@Id
private Long id;
private String name;
// setters, getters, etc
}
对于B表,您必须声明多对一关系和复合id。对于@EmbeddedId
,您不能将实体引用用作字段,因此在这种情况下@IdClass
似乎更好:
@Entity
@IdClass(BId.class)
public class B {
@Id
@ManyToOne
private A master;
@Id
@ManyToOne
private A slave;
// setters, getters, etc
}
最后一个,BId.class。同样,不能将实体引用用作字段,但它们可以替换为A.id类型(长):
如果您需要完全控制表B,那么ilinykhma的方法(
IdClass
)是好的(或者您可以使用EmbeddedId
)
但是如果你“只是”需要“A之间的多对多主/从关系”
(表B的性质表明了这一点。)
…,那么这就更好了:
@Entity
public class A implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToMany
@JoinTable(
name = "B",
joinColumns = @JoinColumn(name = "slave_id"),
inverseJoinColumns = @JoinColumn(name = "master_id"))
private List<A> myMasters;
@ManyToMany(mappedBy = "myMasters")
private List<A> mySlaves;
private String name;
// getter, setter, hashCode, equals...
}
测试:
输出:
...
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.2.RELEASE)
2020-01-11 17:29:53.400 INFO 10532 --- [ main] c.example.soq59695741.ApplicationTests : Starting ApplicationTests on ******* with PID 10532 (started by **** in D:\***\***\***)
2020-01-11 17:29:53.403 INFO 10532 --- [ main] c.example.soq59695741.ApplicationTests : No active profile set, falling back to default profiles: default
2020-01-11 17:29:53.927 INFO 10532 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2020-01-11 17:29:53.956 INFO 10532 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 15ms. Found 0 JPA repository interfaces.
2020-01-11 17:29:54.514 INFO 10532 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-01-11 17:29:54.806 INFO 10532 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-01-11 17:29:54.893 INFO 10532 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-01-11 17:29:54.973 INFO 10532 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.4.9.Final}
2020-01-11 17:29:55.184 INFO 10532 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-01-11 17:29:55.606 INFO 10532 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2020-01-11 17:29:56.327 INFO 10532 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-01-11 17:29:56.335 INFO 10532 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-01-11 17:29:56.509 INFO 10532 --- [ main] c.example.soq59695741.ApplicationTests : Started ApplicationTests in 3.437 seconds (JVM running for 4.615)
2020-01-11 17:29:56.620 INFO 10532 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@3a393455 testClass = ApplicationTests, testInstance = com.example.soq59695741.ApplicationTests@5829e4f4, testMethod = test@ApplicationTests, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@13518f37 testClass = ApplicationTests, locations = '{}', classes = '{class com.example.soq59695741.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@7fd7a283, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@16612a51, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@47c81abf, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@6b0d80ed], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@2fa879ed]; rollback [true]
2020-01-11 17:29:56.796 INFO 10532 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@3a393455 testClass = ApplicationTests, testInstance = com.example.soq59695741.ApplicationTests@5829e4f4, testMethod = test@ApplicationTests, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@13518f37 testClass = ApplicationTests, locations = '{}', classes = '{class com.example.soq59695741.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@7fd7a283, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@16612a51, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@47c81abf, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@6b0d80ed], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.154 s - in com.example.soq59695741.ApplicationTests
2020-01-11 17:29:56.859 INFO 10532 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-01-11 17:29:56.859 INFO 10532 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2020-01-11 17:29:56.861 ERROR 10532 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000478: Unsuccessful: drop table a if exists
2020-01-11 17:29:56.870 INFO 10532 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-01-11 17:29:56.874 INFO 10532 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Results:
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 7.976 s
我以相同的方式将其插入到拥有的实体(仅A表)中,而不是映射到关联的实体(B表)
insert into A (id, name)
values (1, 'aa'),
(2, 'ab'),
(3, 'ac'),
(4, 'ad');
insert into B (master_id, slave_id)
values (1, 2),
(2, 3),
(2, 4);
@SpringBootTest
class ApplicationTests {
@Autowired
private EntityManager entityManager;
@Test
@Transactional
void test() {
assertThat(entityManager);
A one = entityManager.find(A.class, 1L);
assertThat(one);
assertThat(one.getMyMasters().isEmpty());
A two = entityManager.find(A.class, 2L);
assertThat(two);
assertThat(one.getMySlaves().contains(two));
assertThat(two.getMyMasters().contains(one));
}
}
...
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.2.2.RELEASE)
2020-01-11 17:29:53.400 INFO 10532 --- [ main] c.example.soq59695741.ApplicationTests : Starting ApplicationTests on ******* with PID 10532 (started by **** in D:\***\***\***)
2020-01-11 17:29:53.403 INFO 10532 --- [ main] c.example.soq59695741.ApplicationTests : No active profile set, falling back to default profiles: default
2020-01-11 17:29:53.927 INFO 10532 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2020-01-11 17:29:53.956 INFO 10532 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 15ms. Found 0 JPA repository interfaces.
2020-01-11 17:29:54.514 INFO 10532 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-01-11 17:29:54.806 INFO 10532 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-01-11 17:29:54.893 INFO 10532 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-01-11 17:29:54.973 INFO 10532 --- [ main] org.hibernate.Version : HHH000412: Hibernate Core {5.4.9.Final}
2020-01-11 17:29:55.184 INFO 10532 --- [ main] o.hibernate.annotations.common.Version : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-01-11 17:29:55.606 INFO 10532 --- [ main] org.hibernate.dialect.Dialect : HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
2020-01-11 17:29:56.327 INFO 10532 --- [ main] o.h.e.t.j.p.i.JtaPlatformInitiator : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-01-11 17:29:56.335 INFO 10532 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-01-11 17:29:56.509 INFO 10532 --- [ main] c.example.soq59695741.ApplicationTests : Started ApplicationTests in 3.437 seconds (JVM running for 4.615)
2020-01-11 17:29:56.620 INFO 10532 --- [ main] o.s.t.c.transaction.TransactionContext : Began transaction (1) for test context [DefaultTestContext@3a393455 testClass = ApplicationTests, testInstance = com.example.soq59695741.ApplicationTests@5829e4f4, testMethod = test@ApplicationTests, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@13518f37 testClass = ApplicationTests, locations = '{}', classes = '{class com.example.soq59695741.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@7fd7a283, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@16612a51, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@47c81abf, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@6b0d80ed], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@2fa879ed]; rollback [true]
2020-01-11 17:29:56.796 INFO 10532 --- [ main] o.s.t.c.transaction.TransactionContext : Rolled back transaction for test: [DefaultTestContext@3a393455 testClass = ApplicationTests, testInstance = com.example.soq59695741.ApplicationTests@5829e4f4, testMethod = test@ApplicationTests, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@13518f37 testClass = ApplicationTests, locations = '{}', classes = '{class com.example.soq59695741.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@7fd7a283, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@16612a51, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@47c81abf, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@6b0d80ed], contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map[[empty]]]
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.154 s - in com.example.soq59695741.ApplicationTests
2020-01-11 17:29:56.859 INFO 10532 --- [extShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2020-01-11 17:29:56.859 INFO 10532 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
2020-01-11 17:29:56.861 ERROR 10532 --- [extShutdownHook] .SchemaDropperImpl$DelayedDropActionImpl : HHH000478: Unsuccessful: drop table a if exists
2020-01-11 17:29:56.870 INFO 10532 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2020-01-11 17:29:56.874 INFO 10532 --- [extShutdownHook] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Results:
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
------------------------------------------------------------------------
BUILD SUCCESS
------------------------------------------------------------------------
Total time: 7.976 s