Постоянство каскада гибернации: дочерний объект сохранен, но идентификатор не назначен (только в одном случае) - PullRequest
0 голосов
/ 27 июня 2018

Я запускаю небольшой проект 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...