Если вам нужен полный контроль над таблицей B, тогда подход Ильиных (IdClass
) хорош (в качестве альтернативы вы можете использовать EmbeddedId
).
Но если вам «просто» нужен «Много- Соотношение хозяин / подчиненный ко-многим между "A"
..., тогда это лучше:
@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...
}
Тестовые данные:
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