微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

与@DataJpaTest保持多对多关系,并且h2数据库不起作用

如何解决与@DataJpaTest保持多对多关系,并且h2数据库不起作用

我有两个实体。 User

@Entity
@Table(name = "user")
@Data
public class User {

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

@Column(name = "username",unique = true)
private String username;

@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role",joinColumns = @JoinColumn(name = "user_id"),inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();

public void addRole(Role role) {
    this.roles.add(role);
}

public void addUser(User user) {
    this.users.add(user);
}

Role

@Entity
@Table(name = "role")
@Data
public class Role {

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

@Column(name = "name",unique = true)
private String name;

@ManyToMany(mappedBy = "roles",cascade = {CascadeType.PERSIST,CascadeType.MERGE})
private Set<User> users = new HashSet<>();

我正在使用Spring Data JPA,并为UserRole创建存储库:

public interface UserRepository extends JpaRepository<User,Long> {

public Optional<User> findByUsername(String username);

}
public interface RoleRepository extends JpaRepository<Role,Long> {

public Optional<Role> findByName(String name);

public void deleteByName(String name);

}

我编写了一个测试,该测试创建了一个新的User并将Role添加User,但是Role doesn't get added to the User`,并且测试失败:

@ExtendWith(SpringExtension.class)
@DataJpaTest
class UserRepositoryTest {

@Autowired
UserRepository userRepository;
@Autowired
RoleRepository roleRepository;

@BeforeEach
void setUp() {

    Role role1 = new Role();
    role1.setName("Admin");

    Role role2 = new Role();
    role2.setName("Teacher");

    Role role3 = new Role();
    role3.setName("User");

    roleRepository.saveAll(Arrays.asList(role1,role2,role3));

}

@Test
@Transactional
public void createNewUsertest() {

    String username = "User1";
    String password = "password";
    String firstName = "name";
    String lastName = "last name";

    User user = new User();
    user.setUsername(username);
    user.setPassword(password);
    user.setFirstName(firstName);
    user.setLastName(lastName);

    Role findedUserRole = roleRepository.findByName("User").get();
    Role findedAdminRole = roleRepository.findByName("Admin").get();

    user.addRole(findedUserRole);
    user.addRole(findedAdminRole);

    userRepository.save(user);

    User findedUser = userRepository.findByUsername(username).get();
    Role findedRole = roleRepository.findByName("User").get();


    assertEquals(firstName,findedUser.getFirstName());
    assertEquals(2,findedUser.getRoles().size());
    assertTrue(findedRole.getUsers().contains(findedUser));
}
}

assertTrue(findedRole.getUsers().contains(findedUser))失败。 如果在应用程序中使用相同的方法(不向角色添加用户),则一切正常,但在测试中不起作用。

我的应用程序:

@Component
public class InitializeData implements CommandLineRunner {

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Transactional
@Override
public void run(String... args) throws Exception {

    Role role1 = new Role();
    role1.setName("Admin");

    Role role2 = new Role();
    role2.setName("Teacher");

    Role role3 = new Role();
    role3.setName("User");

    roleRepository.saveAll(Arrays.asList(role1,role3));

    String username = "User1";
    String password = "password";
    String firstName = "name";
    String lastName = "last name";

    User user = new User();
    user.setUsername(username);
    user.setPassword(password);
    user.setFirstName(firstName);
    user.setLastName(lastName);

    Role findedUserRole = roleRepository.findByName("User").get();
    Role findedAdminRole = roleRepository.findByName("Admin").get();

    user.addRole(findedUserRole);
    user.addRole(findedAdminRole);

    userRepository.save(user);

}

}

一切正常! 发生了什么事?

解决方法

您陷入了JPA一级缓存的陷阱。

您的完整测试在单个事务中进行,因此在会话中进行。

JPA的核心原则之一是,在给定类和ID的会话中,EntityManager将始终返回相同的实例。

因此,当您在测试中执行Role findedRole = roleRepository.findByName("User").get();时,实际上并没有重新加载角色。您将获得在测试的设置部分创建的确切实例,该实例仍然没有用户。

解决此问题的方法取决于您实际想要实现的目标。

  1. 对于双向关系,应始终保持它们同步,即,对user.addRole(..)的调用应更新作为参数传递的Role。有关详细信息,请参见https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/

    虽然这会使您的测试变为绿色,但实际上并不会测试数据是否已写入数据库,是否可以按预期再次加载。为此,我喜欢使用以下方法之一。

  2. flushclear 。获得注入测试的EntityManager。保存实体后,调用flush,以便将更改写入数据库,然后clear清除一级缓存。这样可以确保后续的加载操作实际上可以访问数据库并加载新实例。

  3. 明确交易。从测试中删除@Transactional批注。使用TransactionManager.executeTransactionTemplate注入到测试和运行设置,保存部分以及装入和声明部分中,分别进行事务处理。我认为这使测试的意图更加明显。但是现在您的事务实际上已经提交,如果您将同一个数据库重新用于多个测试,则可能会导致问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。