Я запускаю небольшой проект Spring Boot с Hibernate, и я создал родительскую сущность GameDefinition
, которая будет управлять устойчивостью своих детей:
@Entity
@Table(name = "game")
public class GameDefinition {
@Id
@Column(name = "game_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// other fields
@OneToMany(mappedBy = "gameDefinition", fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<GamePointDefinition> pointDefinitions = new ArrayList<>();
// getters and setters
public void addGamePointDefinition(GamePointDefinition entity) {
this.pointDefinitions.add(entity);
entity.setGameDefinition(this);
}
}
И его дочерняя сущность GamePointDefinition
:
@Entity
@Table(name = "game_pointdef")
public class GamePointDefinition {
@Id
@Column(name = "game_pointdef_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "game_id", nullable = false)
private GameDefinition gameDefinition;
// other fields
// getters and setters
public void setGameDefinition(GameDefinition gameDefinition) {
this.gameDefinition = gameDefinition;
}
}
Я создал компонент хранилища, используя объект Spring Data Repository
:
public interface GameDefinitionRepository extends Repository<GameDefinition, Long> {
Optional<GameDefinition> findById(Long id);
GameDefinition save(GameDefinition entity);
}
Я настраиваю некоторые модульные тесты с SpringRunner
и использую H2 в качестве базы данных (Hibernate генерирует схему)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = { Application.class, TestConfiguration.class })
public class GameDefinitionRepositoryTest {
@Autowired
private GameDefinitionRepository repository;
@Autowired // util bean defined in TestConfiguration
private IntegrationUtil integrationUtil;
@Before
public void init() {
integrationUtil.truncateDatabase();
}
// other passing tests
@Test
public void testCascadeAddGamePointDefinitions() {
GameDefinition saved = new GameDefinition();
saved.setName(...); // set other fields
GamePointDefinition point1 = new GamePointDefinition();
point1.setName(...); // set other fields
GamePointDefinition point2 = new GamePointDefinition();
point2.setName(...); // set other fields
saved.addGamePointDefinition(point1);
saved.addGamePointDefinition(point2);
saved = repository.save(saved);
GameDefinition fetched = repository.findById(saved.getId()).get();
assertEquals(2, fetched.getPointDefinitions().size());
assertThat(fetched.getPointDefinitions(), hasItem(hasProperty("id", is(point1.getId()))));
assertThat(fetched.getPointDefinitions(), hasItem(hasProperty("id", is(point2.getId()))));
GamePointDefinition point3 = new GamePointDefinition(); // (**)
point3.setName(...); // set other fields
saved.addGamePointDefinition(point3);
saved = repository.save(saved);
fetched = repository.findById(saved.getId()).get();
assertEquals(3, fetched.getPointDefinitions().size());
assertThat(fetched.getPointDefinitions(), hasItem(hasProperty("id", is(point1.getId()))));
assertThat(fetched.getPointDefinitions(), hasItem(hasProperty("id", is(point2.getId()))));
assertThat(fetched.getPointDefinitions(), hasItem(hasProperty("id", is(point3.getId())))); // (*)
}
}
Когда я запускаю тестовый класс, мой testCascadeAddGamePointDefinitions
завершается ошибкой со следующей ошибкой:
java.lang.AssertionError:
Expected: a collection containing hasProperty("id", is null)
but: property 'id' was <5L>, property 'id' was <6L>, property 'id' was <7L>
at ....
Ошибка выдается в строке, помеченной // (*)
. Это означает, что объект point3
был сохранен, и у него есть идентификатор в БД, но id
в объекте по-прежнему равен нулю . Странно то, что point1
и point2
объекты действительно получают id
, установленное DB . (подтверждено в отладчике).
Кроме того, использование выбранной версии родительского элемента вместо сохраняемого первого объекта (т. Е. saved = fetched;
в строке непосредственно перед объектом с // (**)
) возвращает ту же ошибку в том же месте.
Отладка, я обнаружил, что point1
и point2
были вставлены оригинальные ArrayList
, и после save
, ArrayList
становится PersistentBag
, который содержит исходные point1
и point2
экземпляры , Но при втором сохранении содержимое PersistentBag
становится 3 новыми объектами, отличающимися (в памяти) от тех, которые я создаю в тесте.
Почему он действует по-разному в каждом случае? И есть возможность сохранить и назначить идентификатор для объекта point3
?
PD: тестирование application.properties
:
# Database configuration
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
# Hibernate configuration
spring.jpa.hibernate.ddl-auto=create