Dropwizard org.hibernate.LazyInitializationException: 延迟初始化集合失败无法初始化代理 - 无会话
Dropwizard org.hibernate.LazyInitializationException: failed to lazily initialize a collection could not initialize proxy - no Session
目前我正在开发一个带休眠功能的 dropwizard API。我在两个模型之间定义了多对多关系:User
和 Role
.
我有以下数据库结构:
当我尝试检索数据库中的所有用户时,我得到 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();
}
}
}
目前我正在开发一个带休眠功能的 dropwizard API。我在两个模型之间定义了多对多关系:User
和 Role
.
我有以下数据库结构:
当我尝试检索数据库中的所有用户时,我得到 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();
}
}
}