Dropwizard org.hibernate.LazyInitializationException: 延迟初始化集合失败无法初始化代理 - 无会话

Dropwizard org.hibernate.LazyInitializationException: failed to lazily initialize a collection could not initialize proxy - no Session

目前我正在开发一个带休眠功能的 dropwizard API。我在两个模型之间定义了多对多关系:UserRole.

我有以下数据库结构:

ERD

当我尝试检索数据库中的所有用户时,我得到 LazyInitializationException。根据 Dropwizard documentation 我需要在我的资源的 @UnitOfWork 注释处。这仍然没有解决我的问题。

当我将关系的 fetchType 更改为 FetchType.EAGER 时,我实际上得到了结果,但是它 returns 重复结果。

我该如何解决这个问题。我的关系定义正确还是我在这里犯了一个大错误?显然我遗漏了一些东西。

这是我的 User.java class:

@Entity
@Table(name = "\"user\"")
public class User implements Principal {

    @Id @GeneratedValue @Column(name = "id")
    private long id;

    @NotEmpty @Email @JsonView(View.Public.class) @Column(name = "email")
    private String email;

    @NotEmpty @JsonView(View.Protected.class) @Column(name = "password")
    private String password;

    @ManyToMany(targetEntity = Role.class)
    @JoinTable(name = "user_role",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;

    public long getId()
    {
        return id;
    }

    public void setPassword(String password)
    {
        this.password = password;
    }

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

    public String getEmail()
    {
        return email;
    }

    public void setEmail(String email)
    {
        this.email = email;
    }

    public String getPassword()
    {
        return password;
    }

    @JsonIgnore
    public String getName()
    {
        return email;
    }

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

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

    public boolean hasRole(String roleName) {
        if (roles != null)
        {
            for(Role role : roles)
            {
                if(role.getName().equals(roleName))
                {
                    return true;
                }
            }
        }

        return false;
    }
}

这是我的 Role.java class:

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

    @Id @GeneratedValue @Column(name = "id") @JsonView(View.Public.class)
    private long id;

    @JsonView(View.Public.class) @Column(name = "name", nullable = false, length = 255)
    private String name;

    @ManyToMany(targetEntity = User.class, mappedBy = "roles")
    @JsonIgnore
    private Set<User> users = new HashSet<>();

    public Role() {
        // Empty constructor
    }

    public Role(long id, String name) {
        this.id = id;
        this.name = name;
    }

    @JsonProperty
    public long getId() {
        return id;
    }

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

    @JsonProperty
    public String getName() {
        return name;
    }

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

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

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

用户资源:

@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
    private final UserDAO dao;

    public UserResource(UserDAO userDAO)
    {
        dao = userDAO;
    }

    @GET
    @JsonView(View.Public.class)
    @RolesAllowed("beheerder")
    @UnitOfWork
    @Timed
    public List<User> retrieveAll(@Auth User user)
    {
        return dao.findAll();
    }

    @GET
    @Path("/{id}")
    @JsonView(View.Public.class)
    @UnitOfWork
    public User retrieve(@PathParam("id") int id)
    {
        return dao.findById(id);
    }

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @JsonView(View.Protected.class)
    @UnitOfWork
    public void create(User user)
    {
        dao.create(user);
    }

    @PUT
    @Path("/{id}")
    @Consumes(MediaType.APPLICATION_JSON)
    @JsonView(View.Protected.class)
    @UnitOfWork
    public void update(@PathParam("id") int id, @Auth User authenticator, User user)
    {
        dao.update(authenticator, id, user);
    }

    @DELETE
    @Path("/{id}")
    @RolesAllowed("beheerder")
    @UnitOfWork
    public void delete(@PathParam("id") int id)
    {
        dao.delete(id);
    }

    @GET
    @Path("/me")
    @JsonView(View.Private.class)
    @UnitOfWork
    public User authenticate(@Auth User authenticator)
    {
        return authenticator;
    }
}

这里是个例外:

ERROR [2016-01-11 20:15:23,396] io.dropwizard.jersey.errors.LoggingExceptionMapper: Error handling a request: a2a5aa2494999e37
! org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: nl.hsleiden.ipsen3.core.User.roles, could not initialize proxy - no Session
! at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:576) ~[hibernate-core-4.3.11.Final.jar:4.3.11.Final]

看来我调试错了。它与 AuthorizationService.

有关

在我的 UserResource class 中,我有一个 @Auth@RolesAllowed 注释来检查是否允许用户访问该资源。

AuthenticationService 调用了 UserDAO,但由于某种原因没有 Hibernate 会话,尽管该资源已用 @UnitOfWork 注释。所以我不得不打开自己的会话并在用户模型中初始化集合。

这感觉像是一个 hack,但它似乎有效。

这里有一些代码可以为您提供一些上下文:

AuthenticationService

public class AuthenticationService implements Authenticator<BasicCredentials, User>, Authorizer<User> {

    private final UserDAO userDAO;

    public AuthenticationService(UserDAO userDAO) {
        this.userDAO = userDAO;
    }

    public Optional<User> authenticate(BasicCredentials credentials)
        throws AuthenticationException {
        User user = userDAO.getByEmail(credentials.getUsername());
        if (user != null && user.getPassword().equals(credentials.getPassword())) {
            return Optional.of(user);
        }
        return Optional.absent();
    }

    public boolean authorize(User user, String role) {
        return user.hasRole(role);
    }
}

UserResource

@Path("/users")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource {
    private final UserDAO dao;

    public UserResource(UserDAO userDAO)
    {
        dao = userDAO;
    }

    @GET
    @UnitOfWork
    @JsonView(View.Public.class)
    @RolesAllowed("beheerder")
    public List<User> retrieveAll(@Auth User user)
    {
        List<User> users = dao.findAll();
        for (User u: users) {
            Hibernate.initialize(u.getRoles());
        }
        return users;
    }

    // Some other routes..
}

UserDAO

public class UserDAO extends AbstractDAO<User> {
    private final SessionFactory sessionFactory;

    /**
     * Creates a new DAO with a given session provider.
     *
     * @param sessionFactory a session provider
     */
    public UserDAO(SessionFactory sessionFactory)
    {
        super(sessionFactory);
        this.sessionFactory = sessionFactory;
    }

    /**
     * Retrieve all users.
     *
     * @return
     */
    public List findAll()
    {
        return criteria().list();
    }

    /**
     * Finds a user by email. Used for authorization.
     *
     * @param email
     * @return
     */
    public User getByEmail(String email)
    {
        Session session = sessionFactory.openSession();
        try {
            User user = (User) session.createCriteria(User.class).add(Restrictions.eq("email", email))
                .uniqueResult();
            Hibernate.initialize(user.getRoles());
            return user;
        } finally {
            session.clear();
        }
    }
}