lukassko
lukassko

Reputation: 135

Hibernate ManyToMany and Transactional annotation

I try to test my @Repository layer using jUnit and integration testing. In my entity classes I use @ManyToMany bidirectional annotation. My test class I have annotated with @Transactional. When I executing persist method on the parent side of relationship the User object and Role are inserted to databse but there is no relations in join table. When I move @Transactional annotation to @Repository class everything works ok. Maybe someone can exaplain me what is the reason why it is no working when @Transactional annotation is on test class?

User entity:

@Table(name = "users")
@Entity
public class User extends BaseEntity  {

    ...

    @ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
    @JoinTable(
        name = "users_roles", 
        joinColumns = { @JoinColumn(name = "user_id") }, 
        inverseJoinColumns = { @JoinColumn(name = "role_id") }
    )
    private Set<Role> roles;

    .. getters and setters 

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

    public Set<Role> getRoles() {
        if (this.roles == null) {
            this.roles = new HashSet<>();
        }
        return this.roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

Role entity

@Table(name = "roles")
@Entity
public class Role extends BaseEntity {

    public enum UserRole {
        ADMIN, USER
    }

    @Enumerated(EnumType.STRING)
    @Column(name="role")
    private UserRole role;

    @ManyToMany(mappedBy = "roles", fetch = FetchType.EAGER,cascade = CascadeType.ALL)
    private Set<User> users;

    ... getters and setters
}

Test class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {MysqlPersistanceConfig.class})
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Transactional
public class UserRepositoryIT {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void stage10_saveOneUserAndCheckIfWasSavedTest() {
        User user = new User();
        user.setName("admin");
        user.setPassword("root");

        Role role1 = new Role(UserRole.USER);
        Role role2 = new Role(UserRole.ADMIN);

        user.addRole(role1);
        user.addRole(role2);

        Assert.assertTrue(role1.getUsers().size() == 1);
        Assert.assertTrue(role2.getUsers().size() == 1);
        Assert.assertTrue(user.getRoles().size() == 2);

        userRepository.save(user);

        Assert.assertEquals(user, userRepository.find(1));
        Assert.assertEquals(user, userRepository.findByName("admin"));
        Assert.assertEquals(2, userRepository.findByName("admin").getRoles().size());
    }
}

DAO layer:

@Repository
//@Transactional
public class UserRepositoryImpl implements UserRepository {

    @PersistenceContext
    private EntityManager manager;

    @Override
    public void save(User user) {
        if(user.isNew()) {
            manager.persist(user);
        } else {
            manager.merge(user);
        }
    }

    @Override
    public User find(int id) {
        return manager.find(User.class, id);
    }

    @Override
    public User findByName(String name) {
        String queryString = "SELECT u FROM User u WHERE u.name = :name";
        Query query = manager.createQuery(queryString);

        query.setParameter("name", name);

        return (User) query.getSingleResult();
    }
}

Upvotes: 0

Views: 661

Answers (1)

Karol Dowbecki
Karol Dowbecki

Reputation: 44952

Spring's JUnit is rolling back the transaction spanning the test method by default. If you want to check it use EntityManager manager in the test and call manager.flush() or add @Rollback(false) to test method.

Upvotes: 1

Related Questions