Dropwizard org.hibernate.LazyInitializationException:懒得初始化集合无法初始化代理 - 没有Session

时间:2016-01-11 20:50:42

标签: java hibernate dropwizard

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

我有以下数据库结构:

ERD

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

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

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

这是我的User.java课程:

@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课程:

@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);
    }
}

UserResource:

@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]

1 个答案:

答案 0 :(得分:0)

我似乎错误地调试了这个问题。它与AuthorizationService

有关

在我的UserResource课程中,我有@Auth@RolesAllowed注释,以检查是否允许用户访问此资源。

AuthenticationService调用了UserDAO但不知何故没有Hibernate会话,尽管资源是用@UnitOfWork注释的。所以我必须打开自己的会话并在用户模型中初始化集合。

这感觉就像一个黑客,但它似乎工作。

以下是一些为您提供某些背景信息的代码:

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();
        }
    }
}