Spring JPA OneToOne отображает сущности, которые не работают должным образом в транзакционных тестах - PullRequest
0 голосов
/ 18 марта 2019

Итак, я пишу тесты для некоторой логики вокруг некоторых Spring JpaRepositories, которые я создал. Я делаю это, используя встроенную базу данных H2 в памяти. Я вижу странный сценарий, когда мои сопоставленные объекты OneToOne не работают должным образом.

My User и SSOUser - это объекты, объединенные с помощью отношения OneToOne, причем объект SSOUser просто имеет дополнительные метаданные для пользователя, который выполняет вход в наше решение SSO. В приведенном ниже тестовом коде, когда @Transactional НЕ применяется, все работает. Однако, когда я добавляю @Transactional, в закомментированной строке произойдет сбой:

@Test
public void test_saveFindUserAndSsoUser() throws Exception {
    User user = mockUser();
    user = userCarrierRepository.saveAndFlush(user);
    SSOUser ssoUser = new SSOUser();
    ssoUser.setUser(user);
    ssoUser = ssoUserRepository.saveAndFlush(ssoUser);

    assertNotNull(user.getId());
    assertNotNull(ssoUser.getId());
    SSOUser ssoUser2 = ssoUserRepository.findById(ssoUser.getId())
            .orElseThrow(() -> new Exception("Could not find SSO User by ID"));
    assertThat(ssoUser2, allOf(
            hasProperty("id", equalTo(ssoUser.getId())),
            hasProperty("user", notNullValue())
    ));
    assertThat(ssoUser2.getUser(), hasProperty("id", equalTo(user.getId())));

    final User user2 = userCarrierRepository.findById(user.getId())
            .orElseThrow(() -> new Exception("Could not find User by ID"));

    assertThat(user2, allOf(
            hasProperty("id", equalTo(user.getId())),
            hasProperty("ssoUser", notNullValue()) // FAILS HERE BECAUSE ssoUser IS NULL
    ));
    assertThat(user2.getSsoUser(), hasProperty("id", equalTo(ssoUser.getId())));
}

Субъект User, использующий Lombok, имеет вид:

@Data
@Entity
@Table(name="user")
public class User implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY, mappedBy = "user")
    private SSOUser ssoUser;

    public boolean isSSOUser() {
        return ssoUser != null;
    }
}

Объект SSOUser, использующий Lombok, имеет вид:

@Data
@Entity
@Table(name = "sso_user")
public class SSOUser implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

}

А вот оператор SQL DDL для добавления соответствующей таблицы в нашу базу данных:

CREATE TABLE sso_user (
  id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  PRIMARY KEY (id),
  FOREIGN KEY (user_id) REFERENCES public.user (id)
);

Итак, вернемся к исходному коду, объект User сохраняется с полем ssoUser как ноль. Однако объект SSOUser сохраняется с действительным объектом User. Я ожидаю, что таблица sso_user будет содержать соответствующее значение в столбце user_id.

Из-за того, что данные в таблице sso_user верны, я бы ожидал, что userCarrierRepository.findById(...) вернет User с включенным ssoUser, как я ожидал, sslUserRepository.findById(...) вернет SSOUser с включенным user в комплекте.

Однако это не то, что происходит. Только SSOUser возвращается с присоединенной сущностью, пользователь - нет.

Опять же, это происходит только в том случае, если я добавлю @Transactional к приведенному здесь коду. Без этой аннотации все работает так, как я ожидал.

Я надеюсь, что кто-то может объяснить это для меня. Большое вам спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...