Reputation: 65
I am working on a web application using spring 3 and hibernate 4. I am having trouble lazy-loading a set of granted permissions of a group to which a user belongs. When a user object is retrieved from the database, its Group object doesn't have any permissions even though user.getGroup().getPermissions() is invoked explicitly.
I noticed that in debug mode, if I mouse-over the User object and then navigate to the Group object inside the User object, and then navigate to its permissions, I can see its type is shown as PersistentSet, and expanding it will load permissions properly. But in non-debug mode, permissions are never lazy-loaded. What am I missing here? Thanks in advance.
Here are the relationships among the entities: Users have an one-to-many relationship with groups, and groups have a many-to-many relationship with permissions.
Here's the definition of the UserDaoImpl class
@Repository
@Transactional
public class UserDaoImpl implements UserDao {
@Autowired
private SessionFactory sessionFactory;
private Session getSession() {
return sessionFactory.getCurrentSession();
}
@Override
public User get(String username) {
Session session = getSession();
User user = (User) session.createCriteria(User.class).add(Restrictions.eq("username", username)).uniqueResult();
Group g = user.getGroup();
// calling g.getGrantedPermissions() doesn't load any permission
Set<Permission> permissions = g.getGrantedPermissions();
return user;
}
}
Here's the definition of the Permission class
@Entity
public class Permission {
@Id
@GeneratedValue
private Long id;
@Column
private String name;
@Column
private String description;
public Permission()
{
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Here's the definition of the Group class
@Entity
@Table(name="\"Group\"")
public class Group {
public static final String ROLE_VALID_USER = "ROLE_VALID_USER";
@Id
@GeneratedValue
private Long id;
@Column
private String description;
@Column
private String role;
@ManyToMany
@JoinTable(name = "granted_permission",
joinColumns = {@JoinColumn(name = "GROUP_ID", nullable = false, updatable = false) },
inverseJoinColumns = { @JoinColumn(name = "PERMISSION_ID", nullable = false, updatable = false) })
private Set<Permission> grantedPermissions;
public Group()
{
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Set<Permission> getGrantedPermissions() {
return grantedPermissions;
}
public void setGrantedPermissions(Set<Permission> grantedPermissions) {
this.grantedPermissions = grantedPermissions;
}
}
Here's the definition of the User class
@Entity
public class User {
@Id
@Column(name="USERNAME")
private String username;
@Column(name="PASSWORD")
private String password;
@Transient
private final boolean enabled = true;
@Column
private String name;
@Column(name="EMAIL")
private String email;
@Column(name="PHONE")
private String phone;
@ManyToOne
private Group group;
@ManyToOne
@JoinColumn(name="ORIGINAL_BRANCH_ID")
private Branch originalBranch;
@ManyToOne
@JoinColumn(name="ID_OF_RESPONSIBLE_BRANCH")
private Branch responsibleBranch;
public User()
{
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return group.getRole();
}
public boolean isEnabled() {
return enabled;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Group getGroup() {
return group;
}
public void setGroup(Group group) {
this.group = group;
}
public Branch getOriginalBranch() {
return originalBranch;
}
public void setOriginalBranch(Branch originalBranch) {
this.originalBranch = originalBranch;
}
public Branch getResponsibleBranch() {
return responsibleBranch;
}
public void setResponsibleBranch(Branch responsibleBranch) {
this.responsibleBranch = responsibleBranch;
}
}
Here's the persistence configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<tx:annotation-driven />
<context:annotation-config />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<context:component-scan base-package="net.acme.prs" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="mysqlDataSource" />
<property name="packagesToScan" value="net.acme.prs" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQL5InnoDBDialect
</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="mysqlDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/prs" />
<property name="username" value="" />
<property name="password" value="" />
</bean>
</beans>
Upvotes: 3
Views: 2868
Reputation: 40036
I cannot understand what is your problem because you haven't mentioned any error you encountered. If you simply found there was no SQL to fetch the grantedPermissions
, it is what lazy-fetching is about: It is only fetched when it is accessed, and if it is not accessed, it won't be fetched.
The reason you see it being fetched during debug, is because when you are inspecting the grantedPermissions
in debugger, it is accessing that property, which will trigger the lazy fetching. However, for non-debug mode, if you have no code accessing that property, no fetching is precisely what it supposed to do.
If what you are asking is, you have code in non-debug mode that will access grantedPermissions
but lazy fetching failed when it is accessed, then it is due to the access is out of transaction: Hibernate needs to have an active Session to have lazy fetching happening. If the lazy-fetching happens out of the transaction, it will fail because there is no opened Session. You should revisit your design by considering
Upvotes: 3
Reputation: 1
Follow http://docs.oracle.com/javaee/5/api/javax/persistence/FetchType.html
Example : If you want to auto load "grantedPermissions" in "Group", you have to change your annotation to @ManyToMany(fetch = FetchType.EAGER)
Upvotes: 0