我正在与Hibernate一起启动一个小型Spring Boot项目,并创建了一个父实体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,但是对象中的id
仍然为空。奇怪的是point1
和point2
对象确实得到了DB {em>设置的id
。 (在调试器中确认)。
此外,使用获取的父级版本而不是持久化的第一个对象(即,位于saved = fetched;
之前的行中的// (**)
)会在同一位置返回相同的错误。
调试时,我发现point1
和point2
插入了原始的ArrayList
,并且在save
之后,ArrayList
变成了PersistentBag
包含原始的point1
和point2
实例。但是在第二次保存中,PersistentBag
的内容成为3个新对象,(在内存中)与我在测试中实例化的对象不同。
为什么每种情况下的行为都不一样?是否可以保存ID并将其分配给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