W niestandardowym AuthenticationProvider z mojego wiosennego projektu próbuję odczytać listę uprawnień zalogowanego użytkownika, ale napotykam następujący błąd:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.horariolivre.entity.Usuario.autorizacoes, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:566)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:186)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:545)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:124)
at org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:266)
at com.horariolivre.security.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:45)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
at org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter.attemptAuthentication(UsernamePasswordAuthenticationFilter.java:94)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:211)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Czytając inne tematy stąd w StackOverflow, rozumiem, że dzieje się tak ze względu na sposób, w jaki tego typu atrybuty są obsługiwane przez framework, ale nie mogę znaleźć żadnego rozwiązania dla mojego przypadku. Ktoś może wskazać, co robię źle i co mogę zrobić, aby to naprawić?
Kod mojego niestandardowego dostawcy uwierzytelniania to:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UsuarioHome usuario;
public CustomAuthenticationProvider() {
super();
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
System.out.println("CustomAuthenticationProvider.authenticate");
String username = authentication.getName();
String password = authentication.getCredentials().toString();
Usuario user = usuario.findByUsername(username);
if (user != null) {
if(user.getSenha().equals(password)) {
List<AutorizacoesUsuario> list = user.getAutorizacoes();
List <String> rolesAsList = new ArrayList<String>();
for(AutorizacoesUsuario role : list){
rolesAsList.add(role.getAutorizacoes().getNome());
}
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
for (String role_name : rolesAsList) {
authorities.add(new SimpleGrantedAuthority(role_name));
}
Authentication auth = new UsernamePasswordAuthenticationToken(username, password, authorities);
return auth;
}
else {
return null;
}
} else {
return null;
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
Moje klasy jednostek to:
UsuarioHome.java
@Entity
@Table(name = "usuario")
public class Usuario implements java.io.Serializable {
private int id;
private String login;
private String senha;
private String primeiroNome;
private String ultimoNome;
private List<TipoUsuario> tipoUsuarios = new ArrayList<TipoUsuario>();
private List<AutorizacoesUsuario> autorizacoes = new ArrayList<AutorizacoesUsuario>();
private List<DadosUsuario> dadosUsuarios = new ArrayList<DadosUsuario>();
private ConfigHorarioLivre config;
public Usuario() {
}
public Usuario(String login, String senha) {
this.login = login;
this.senha = senha;
}
public Usuario(String login, String senha, String primeiroNome, String ultimoNome, List<TipoUsuario> tipoUsuarios, List<AutorizacoesUsuario> autorizacoesUsuarios, List<DadosUsuario> dadosUsuarios, ConfigHorarioLivre config) {
this.login = login;
this.senha = senha;
this.primeiroNome = primeiroNome;
this.ultimoNome = ultimoNome;
this.tipoUsuarios = tipoUsuarios;
this.autorizacoes = autorizacoesUsuarios;
this.dadosUsuarios = dadosUsuarios;
this.config = config;
}
public Usuario(String login, String senha, String primeiroNome, String ultimoNome, String tipoUsuario, String[] campos) {
this.login = login;
this.senha = senha;
this.primeiroNome = primeiroNome;
this.ultimoNome = ultimoNome;
this.tipoUsuarios.add(new TipoUsuario(this, new Tipo(tipoUsuario)));
for(int i=0; i<campos.length; i++)
this.dadosUsuarios.add(new DadosUsuario(this, null, campos[i]));
}
@Id
@Column(name = "id", unique = true, nullable = false)
@GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
@Column(name = "login", nullable = false, length = 16)
public String getLogin() {
return this.login;
}
public void setLogin(String login) {
this.login = login;
}
@Column(name = "senha", nullable = false)
public String getSenha() {
return this.senha;
}
public void setSenha(String senha) {
this.senha = senha;
}
@Column(name = "primeiro_nome", length = 32)
public String getPrimeiroNome() {
return this.primeiroNome;
}
public void setPrimeiroNome(String primeiroNome) {
this.primeiroNome = primeiroNome;
}
@Column(name = "ultimo_nome", length = 32)
public String getUltimoNome() {
return this.ultimoNome;
}
public void setUltimoNome(String ultimoNome) {
this.ultimoNome = ultimoNome;
}
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "tipo_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_tipo") })
@LazyCollection(LazyCollectionOption.TRUE)
public List<TipoUsuario> getTipoUsuarios() {
return this.tipoUsuarios;
}
public void setTipoUsuarios(List<TipoUsuario> tipoUsuarios) {
this.tipoUsuarios = tipoUsuarios;
}
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "autorizacoes_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_autorizacoes") })
@LazyCollection(LazyCollectionOption.TRUE)
public List<AutorizacoesUsuario> getAutorizacoes() {
return this.autorizacoes;
}
public void setAutorizacoes(List<AutorizacoesUsuario> autorizacoes) {
this.autorizacoes = autorizacoes;
}
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "dados_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_dados") })
@LazyCollection(LazyCollectionOption.TRUE)
public List<DadosUsuario> getDadosUsuarios() {
return this.dadosUsuarios;
}
public void setDadosUsuarios(List<DadosUsuario> dadosUsuarios) {
this.dadosUsuarios = dadosUsuarios;
}
@OneToOne
@JoinColumn(name="fk_config")
public ConfigHorarioLivre getConfig() {
return config;
}
public void setConfig(ConfigHorarioLivre config) {
this.config = config;
}
}
AutorizacoesUsuario.java
@Entity
@Table(name = "autorizacoes_usuario", uniqueConstraints = @UniqueConstraint(columnNames = "id"))
public class AutorizacoesUsuario implements java.io.Serializable {
private int id;
private Usuario usuario;
private Autorizacoes autorizacoes;
public AutorizacoesUsuario() {
}
public AutorizacoesUsuario(Usuario usuario, Autorizacoes autorizacoes) {
this.usuario = usuario;
this.autorizacoes = autorizacoes;
}
@Id
@Column(name = "id", unique = true, nullable = false)
@GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
@OneToOne
@JoinColumn(name = "fk_usuario", nullable = false, insertable = false, updatable = false)
public Usuario getUsuario() {
return this.usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
@OneToOne
@JoinColumn(name = "fk_autorizacoes", nullable = false, insertable = false, updatable = false)
public Autorizacoes getAutorizacoes() {
return this.autorizacoes;
}
public void setAutorizacoes(Autorizacoes autorizacoes) {
this.autorizacoes = autorizacoes;
}
}
Autorizacoes.java
@Entity
@Table(name = "autorizacoes")
public class Autorizacoes implements java.io.Serializable {
private int id;
private String nome;
private String descricao;
public Autorizacoes() {
}
public Autorizacoes(String nome) {
this.nome = nome;
}
public Autorizacoes(String nome, String descricao) {
this.nome = nome;
this.descricao = descricao;
}
@Id
@Column(name = "id", unique = true, nullable = false)
@GeneratedValue(strategy=GenerationType.AUTO)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
@Column(name = "nome", nullable = false, length = 16)
public String getNome() {
return this.nome;
}
public void setNome(String nome) {
this.nome = nome;
}
@Column(name = "descricao", length = 140)
public String getDescricao() {
return this.descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
}
Pełny projekt dostępny na github
java
spring
hibernate
spring-mvc
spring-security
Kleber Mota
źródło
źródło
@ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
Odpowiedzi:
Musisz dodać
fetch=FetchType.EAGER
wewnątrz adnotacji ManyToMany, aby automatycznie wycofywać elementy podrzędne:@ManyToMany(fetch = FetchType.EAGER)
Lepszą opcją byłoby zaimplementowanie wiosennego menedżera transakcji poprzez dodanie do pliku konfiguracyjnego wiosny:
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven />
Następnie możesz dodać adnotację @Transactional do swojej metody uwierzytelniania, na przykład:
@Transactional public Authentication authenticate(Authentication authentication)
Spowoduje to rozpoczęcie transakcji bazy danych na czas trwania metody uwierzytelniania, umożliwiając pobranie dowolnej leniwej kolekcji z bazy danych podczas próby ich użycia.
źródło
Najlepszym sposobem obsługi tego
LazyInitializationException
jest użycieJOIN FETCH
dyrektywy dla wszystkich jednostek, które musisz pobrać.W każdym razie NIE używaj następujących Anty-wzorców, zgodnie z sugestiami niektórych odpowiedzi:
hibernate.enable_lazy_load_no_trans
Czasami projekcja DTO jest lepszym wyborem niż pobieranie jednostek, a w ten sposób nie otrzymasz żadnej
LazyInitializationException
.źródło
FetchType.EAGER
do swoich skojarzeń. JOIN FETCH jest przeznaczony dlaFetchType.LAZY
skojarzeń, które muszą być chętnie pobierane w czasie zapytania.Dodanie następującej właściwości do pliku persistence.xml może tymczasowo rozwiązać problem
<property name="hibernate.enable_lazy_load_no_trans" value="true" />
Jak powiedział @ vlad-mihalcea, jest to anty-wzór i nie rozwiązuje całkowicie problemu leniwej inicjalizacji, zainicjuj swoje powiązania przed zamknięciem transakcji i użyj zamiast tego DTO.
źródło
Ja też miałem ten problem, kiedy robiłem testy jednostkowe. Bardzo prostym rozwiązaniem tego problemu jest użycie adnotacji @Transactional, która utrzymuje sesję otwartą do końca wykonania.
źródło
Powodem jest to, że gdy używasz leniwego ładowania, sesja jest zamykana.
Istnieją dwa rozwiązania.
Nie używaj leniwego obciążenia.
Ustaw
lazy=false
w XML lub Ustaw w@OneToMany(fetch = FetchType.EAGER)
adnotacji.Użyj leniwego obciążenia.
Ustaw
lazy=true
w XML lub Ustaw w@OneToMany(fetch = FetchType.LAZY)
adnotacji.i dodaj
OpenSessionInViewFilter filter
swojeweb.xml
Szczegóły Zobacz mój post.
https://stackoverflow.com/a/27286187/1808417
źródło
Twoja klasa Custom AuthenticationProvider powinna być oznaczona następującą adnotacją:
Zapewni to również obecność sesji hibernacji.
źródło
Możesz użyć hibernacji leniwego inicjatora.
Poniżej znajduje się kod, do którego możesz się odwołać.
Oto
PPIDO
obiekt danych, który chcę odzyskaćHibernate.initialize(ppiDO); if (ppiDO instanceof HibernateProxy) { ppiDO = (PolicyProductInsuredDO) ((HibernateProxy) ppiDO).getHibernateLazyInitializer() .getImplementation(); ppiDO.setParentGuidObj(policyDO.getBasePlan()); saveppiDO.add(ppiDO); proxyFl = true; }
źródło
Dla tych, którzy mają ten problem ze zbieraniem wyliczeń, oto jak go rozwiązać:
@Enumerated(EnumType.STRING) @Column(name = "OPTION") @CollectionTable(name = "MY_ENTITY_MY_OPTION") @ElementCollection(targetClass = MyOptionEnum.class, fetch = EAGER) Collection<MyOptionEnum> options;
źródło
Przede wszystkim chciałbym powiedzieć, że wszyscy użytkownicy, którzy mówili o lenistwie i transakcjach, mieli rację. Ale w moim przypadku była niewielka różnica polegająca na tym, że użyłem wyniku metody @Transactional w teście i była to poza rzeczywistą transakcją, więc dostałem ten leniwy wyjątek.
Moja metoda obsługi:
@Transactional User get(String uid) {};
Mój kod testowy:
User user = userService.get("123"); user.getActors(); //org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role
Moim rozwiązaniem było umieszczenie tego kodu w innej transakcji, takiej jak ta:
List<Actor> actors = new ArrayList<>(); transactionTemplate.execute((status) -> actors.addAll(userService.get("123").getActors()));
źródło
Uważam, że zamiast umożliwiać szybkie pobieranie, sensowne jest ponowne zainicjowanie jednostki tam, gdzie jest to konieczne, aby uniknąć
LazyInitializationException
wyjątkuźródło
Dla tych, którzy używają JaVers , mając sprawdzoną klasę jednostki, możesz chcieć zignorować właściwości powodujące
LazyInitializationException
wyjątek (np. Używając@DiffIgnore
adnotacji).Informuje to framework, aby ignorował te właściwości podczas obliczania różnic obiektów, więc nie będzie próbował odczytać z bazy danych powiązanych obiektów poza zakresem transakcji (powodując w ten sposób wyjątek).
źródło
Powszechną praktyką jest umieszczanie
@Transactional
klasy powyżej klasy usług.@Service @Transactional public class MyServiceImpl implements MyService{ ... }
źródło
Dodaj adnotację
@JsonManagedReference
Na przykład:
@ManyToMany(cascade=CascadeType.ALL) @JoinTable(name = "autorizacoes_usuario", joinColumns = { @JoinColumn(name = "fk_usuario") }, inverseJoinColumns = { @JoinColumn(name = "fk_autorizacoes") }) @JsonManagedReference public List<AutorizacoesUsuario> getAutorizacoes() { return this.autorizacoes; }
źródło