简体   繁体   中英

Hibernate @Many-To-Many fetching with associations (stackoverflow)

I get a stackoverflow error if I set fetch mode to EAGER and if I don't set EAGER it throws another error . How to arrange a bidirectional association to navigate in both directions?

@Entity
@Table(name = "test_users")
public class User implements Serializable {

    public User(String name, Set<Role> roles) {
        this.name = name;
        this.roles = roles;
    }

    public User() {
    }
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    @ManyToMany
    @JoinTable(name = "test_join", joinColumns = @JoinColumn(name = "user_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles = new HashSet<>();

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<Role> getRoles() {
        return roles;
    }

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

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + this.id;
        hash = 31 * hash + Objects.hashCode(this.name);
        hash = 31 * hash + Objects.hashCode(this.roles);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final User other = (User) obj;
        if (this.id != other.id) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        if (!Objects.equals(this.roles, other.roles)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "User{" + "id=" + id + ", name=" + name + ", roles=" + roles + '}';
    }

}

@Entity
@Table(name = "test_roles")
public class Role implements Serializable {

    public Role(String name, Set<User> users) {
        this.name = name;
        this.users = users;
    }

    public Role() {
    }
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;
    private String name;
    @ManyToMany
    @JoinTable(name = "test_join", joinColumns = @JoinColumn(name = "role_id"),
            inverseJoinColumns = @JoinColumn(name = "user_id"))
    private Set<User> users = new HashSet<>();

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<User> getUsers() {
        return users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 83 * hash + this.id;
        hash = 83 * hash + Objects.hashCode(this.name);
        hash = 83 * hash + Objects.hashCode(this.users);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Role other = (Role) obj;
        if (this.id != other.id) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        if (!Objects.equals(this.users, other.users)) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "Role{" + "id=" + id + ", name=" + name + ", users=" + users + '}';
    }

}

    @Repository(value = "userdao")
@Transactional
public class UserDaoImpl implements UserDao {

    public UserDaoImpl(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public UserDaoImpl() {
    }

    @Autowired
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    private Session currentSession() {
        return sessionFactory.getCurrentSession();
    }

    @Override
    public void addUser(User user) {
        currentSession().save(user);
    }

    @Override
    @Transactional(readOnly = true)
    public List<User> getAllUsers() {
        return currentSession().createQuery("FROM User u").list();
    }

    @Override
    public void updateUser(User user) {
        currentSession().update(user);
    }

    @Override
    public void deleteUser(User user) {
        currentSession().delete(user);
    }

    @Override
    public User getUserByName(String name) {
        return (User) currentSession().createQuery("FROM User WHERE name = '" + name + "'").uniqueResult();
    }

    @Override
    public User getUserById(int id) {
        return (User) currentSession().createQuery("FROM User WHERE id = " + id).uniqueResult();
    }

}

public class Main {

    public static void main(String[] args) {

        UserDao userDao = (UserDao) SpringContext.getContext().getBean("userdao");

        List<User> users = userDao.getAllUsers();
        for (User user : users) {
            System.out.println(user);
        }

    }
}

INFO: HHH000397: Using ASTQueryTranslatorFactory Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.entities.User.roles, could not initialize proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582)

WARN: HHH000100: Fail-safe cleanup (collections) : org.hibernate.engine.loading.internal.CollectionLoadContext@fd53053 Exception in thread "main" java.lang.StackOverflowError at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(Reentra

Shouldn't be difficult. Understand I think you want the User to be the owning entity of the relationship, so mappedBy="roles", and then setting the roles in User is how you persist the relationship. You could query either way, but you don't want to make them EAGER or you will get an unconstrained recursive fetch.

@Entity
public class User {
    @Id @GeneratedValue private int id;
    @ManyToMany private Set<Role> roles;

@Entity
public class Role {
    @Id @GeneratedValue private int id;
    @ManyToMany(mappedBy="roles") private Set<User> users;

and

tx.begin();
Role role1 = new Role();
em.persist(role1);
Role role2 = new Role();
em.persist(role2);
User user1 = new User();
Set<Role> user1Roles = new HashSet<>();
user1Roles.add(role1);
user1.setRoles(user1Roles);
em.persist(user1);

User user2 = new User();
Set<Role> user2Roles = new HashSet<>();
user2Roles.add(role1);
user2Roles.add(role2);
user2.setRoles(user2Roles);
em.persist(user2);
tx.commit();

em.clear();

List<User> users = em.createQuery("select distinct u from User u left join fetch u.roles", User.class).getResultList();

users.forEach( u-> System.out.println(u + ":" + u.getRoles()));

em.close();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM