2017-01-16 2 views
1

У меня проблема с ленивой инициализацией в приложении Spring Boot. У меня есть объект с ленивым полем Role, и у меня есть LazyInitializationException в методе My Spring Security (UserDetailsService), но в контроллере это нормально. Можете ли вы объяснить мне, как работает Spring Boot с fetch = FetchType.LAZY? Почему это не работает Spring Security UserDetailsService и работает в методах контроллера? Я не нашел гида об этом. Благодаря!Spring Boot и fetchType = Lazy

Spring Ботинок:

@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 UserDetailsService:

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

И мой контроллер (все методы работают! Но есть должна excpetion произойти, потому что нет сеанса и Ленивых выборки типа):

@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 в транзакции и возвращала объект, который полностью загружен - со всеми дочерними узлами. этот объект может быть простым компонентом, который построен из класса Users, - hashmap, построенный из объектов или даже строка, как вы хотели вернуться из остального вызова.

+0

Здравствуйте! Spring Data Jpa обертывает методы, такие как testGetUser() в транзакции readOnly = true по умолчанию. И я сделал это в методе getUserByLoginWithRoles. Почему те же методы, что и «testGetUser», не работают в классе безопасности и работают в контроллере? И почему в контроллере я не получаю Lazy исключение с Spring Boot? Почему Spring Boot инициализировал мои ленивые коллекции? Как работает Spring Boot? –

+0

привет. дело в том, что транзакция заканчивается сразу после использования метода весенних данных, так как вы используете ленивую выборку между объектами User и Role, метод весенних данных будет извлекать пользователя без его ролей. если он заключен в более крупный контекст транзакции, то можно пройти по графу пользовательской роли и получить все роли из базы данных. вы можете, очевидно, изменить ленивый выбор, если это возможно, или написать простой запрос hql, который заставляет извлекать это соединение, если вы не хотите делать это нетерпеливо. –

Смежные вопросы