2017-01-16 93 views
1

我的Spring Boot應用程序中存在延遲初始化問題。我有一個懶惰字段Role的實體,我在My Spring Security(UserDetailsService)方法中有LazyInitializationException,但在控制器中沒問題。 您能否向我解釋Spring Boot如何與fetch = FetchType.LAZY搭配使用?爲什麼它不起作用彈簧安全UserDetailsService和工作在控制器方法? 我沒有找到任何關於此的指南。謝謝!Spring Boot和fetchType =懶惰

春季啓動:

@SpringBootApplication 
public class App { 

    public static void main(String[] args) throws Exception { 
     SpringApplication.run(App.class, args); 
    } 
} 

的實體:

@Entity 
@Table(name = "users") 
@Getter 
@Setter 
@NoArgsConstructor 
public class Users { 
    @ManyToMany(fetch = FetchType.LAZY) 
    @JoinTable(name = "Users_Role", joinColumns = @JoinColumn(name = "User_id", referencedColumnName = "id"), 
      inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id")) 
    private Set<Role> roles = new HashSet<Role>(); 
} 

我的服務:

@Transactional(readOnly = true)//does not matter 
public Users getUserByLogin(String login) { 
     return usersRepository.findOneByLogin(login); 
    } 

    @Transactional(readOnly = true) 
    public Users getUserByLoginWithRoles(String login) { 
     Users oneByLogin = usersRepository.findOneByLogin(login); 
     logger.debug("User was initialize with Roles: " + oneByLogin.getRoles().size()); // force initialize of roles and it works! 
     return oneByLogin; 
    } 

    @Transactional(readOnly = true)//does not matter 
    public Users testGetUser() { 
     Users oneByLogin = usersRepository.getOne(1L); 
     return oneByLogin; 
    } 

    @Transactional(readOnly = true)//does not matter 
    public Users testFindUser() { 
     Users oneByLogin = usersRepository.findOne(1L); 
     return oneByLogin; 
    } 

而且我的Spring Security的UserDetailsS​​ervice:

@Service 
    public class UserDetailsServiceImpl implements UserDetailsService { 

    @Autowired 
    private Services services; 

    @Override 
    public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException { 
     Users user; 
     Users userFetchedViaGet = services.testGetUser(); 
     Users userFetchedViaCustomMethod = services.getUserByLogin(login); 
     Users userFetchedViaFind = services.testFindUser(); 
     Users userFetchedWithRoles = services.getUserByLoginWithRoles(login); 
     try { 
      userFetchedViaGet.getRoles().add(new Role("test")); 
     } catch (Exception e) { 
      e.printStackTrace();//LazyInitializationException: failed to lazily initialize a collection of role: , could not initialize proxy - no Session 
     } 
     try { 
      userFetchedViaCustomMethod.getRoles().add(new Role("test")); 
     } catch (Exception e) { 
      e.printStackTrace();//LazyInitializationException: failed to lazily initialize a collection of role: , could not initialize proxy - no Session 
     } 
     try { 
      userFetchedViaFind.getRoles().add(new Role("test")); //LazyInitializationException: failed to lazily initialize a collection of role: , could not initialize proxy - no Session 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     //some code 
     } 
    } 

和我的控制器(所有方法的作品!但有錯誤時拋出發生,因爲沒有會話和懶惰獲取類型):

@RequestMapping(value = "/test", method = RequestMethod.GET) 
    public String test() {  
     Users userFetchedViaGet = services.testGetUser(); 
     Users userFetchedViaCustomMethod = services.getUserByLogin("ADMIN"); 
     Users userFetchedViaFind = services.testFindUser(); 
     Users userFetchedWithRoles = services.getUserByLoginWithRoles("ADMIN"); 
     try { 
      userFetchedViaGet.getRoles().add(new Role("test")); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     try { 
      userFetchedViaCustomMethod.getRoles().add(new Role("test")); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     try { 
      userFetchedViaFind.getRoles().add(new Role("test")); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
     //some code 
    } 
+1

你有沒有試過這個http://stackoverflow.com/questions/11746499/solve-failed-to-lazily-initialize-a-collection-of-role-exception –

+0

你好!這篇文章與我的問題沒有任何共同之處。 –

+0

你能解決這個問題嗎?我實際上有同樣的問題 –

回答

0

你需要一個交易環繞整個事情 - 在你的門面 - 測試()方法用於這項工作,但這不是一個很好的做法。

要走的路是讓您的服務在事務中包裝api方法,並返回一個完全加載的對象 - 包含所有子節點。這個對象可以是一個簡單的bean,它由Users類構建, 由對象構建的哈希映射或甚至像您想要從其餘調用返回的字符串構建。

+0

你好! Spring Data Jpa在事務中包裝testGetUser()方法readOnly =默認情況下爲true。我在getUserByLoginWithRoles方法中做了。爲什麼類似「testGetUser」的方法在Security類中不起作用並在控制器中工作?爲什麼在控制器中,我沒有收到Spring Boot的Lazy異常?爲什麼Spring Boot初始化我的懶惰集合? Spring Boot如何做到這一點? –

+0

嗨。事情就是事務在使用spring數據方法後立即結束,因爲您在User和Role實體之間使用了惰性獲取,Spring數據方法只會在沒有其角色的情況下獲取用戶。如果包裝在較大的事務上下文中,應該可以遍歷用戶角色圖並從數據庫獲取所有角色。在這種情況下,如果可能的話,你可以明顯地將懶惰的獲取改爲渴望,或者編寫一個簡單的hql查詢,如果你不想讓這個渴望的話強制獲取這個連接。 –