En el AuthenticationProvider personalizado de mi proyecto de primavera, estoy intentando leer la lista de autoridades del usuario registrado, pero me enfrento al siguiente error:
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)
Al leer otros temas de aquí en StackOverflow, entiendo que esto sucede debido a la forma en que el marco maneja este tipo de atributo, pero no puedo encontrar ninguna solución para mi caso. ¿Alguien puede señalar qué estoy haciendo mal y qué puedo hacer para solucionarlo?
El código de mi proveedor de autenticación personalizado es:
@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);
}
}
Mis clases de entidad son:
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;
}
}
Proyecto completo disponible en github
java
spring
hibernate
spring-mvc
spring-security
Kleber Mota
fuente
fuente
@ManyToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
Respuestas:
fetch=FetchType.EAGER
Debe agregar dentro de sus anotaciones ManyToMany para retirar automáticamente las entidades secundarias:@ManyToMany(fetch = FetchType.EAGER)
Una mejor opción sería implementar un transactionManager de primavera agregando lo siguiente a su archivo de configuración de primavera:
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:annotation-driven />
Luego puede agregar una anotación @Transactional a su método de autenticación de esta manera:
@Transactional public Authentication authenticate(Authentication authentication)
Esto luego iniciará una transacción de la base de datos durante la duración del método de autenticación, lo que permitirá recuperar cualquier colección diferida de la base de datos cuando intente usarla.
fuente
La mejor manera de manejarlo
LazyInitializationException
es usar laJOIN FETCH
directiva para todas las entidades que necesita buscar.De todos modos, NO use los siguientes Anti-Patrones como lo sugieren algunas de las respuestas:
hibernate.enable_lazy_load_no_trans
A veces, una proyección DTO es una mejor opción que buscar entidades y, de esta manera, no obtendrá ninguna
LazyInitializationException
.fuente
FetchType.EAGER
a sus asociaciones. JOIN FETCH es paraFetchType.LAZY
asociaciones que deben buscarse con entusiasmo en el momento de la consulta.Agregar la siguiente propiedad a su persistence.xml puede resolver su problema temporalmente
<property name="hibernate.enable_lazy_load_no_trans" value="true" />
Como @ vlad-mihalcea dijo que es un antipatrón y no resuelve completamente el problema de la inicialización perezosa, inicialice sus asociaciones antes de cerrar la transacción y use DTO en su lugar.
fuente
Yo también tuve este problema cuando estaba haciendo pruebas unitarias. Una solución muy simple a este problema es usar la anotación @Transactional que mantiene la sesión abierta hasta el final de la ejecución.
fuente
La razón es que cuando usa la carga diferida, la sesión se cierra.
Hay dos soluciones.
No use carga diferida.
Establecer
lazy=false
en XML o Establecer@OneToMany(fetch = FetchType.EAGER)
en anotación.Utilice carga diferida.
Establecer
lazy=true
en XML o Establecer@OneToMany(fetch = FetchType.LAZY)
en anotación.y agrega
OpenSessionInViewFilter filter
tuweb.xml
Detalle Ver mi publicación.
https://stackoverflow.com/a/27286187/1808417
fuente
Puede usar el inicializador perezoso de hibernación.
A continuación se muestra el código que puede consultar.
Aquí
PPIDO
está el objeto de datos que quiero recuperarHibernate.initialize(ppiDO); if (ppiDO instanceof HibernateProxy) { ppiDO = (PolicyProductInsuredDO) ((HibernateProxy) ppiDO).getHibernateLazyInitializer() .getImplementation(); ppiDO.setParentGuidObj(policyDO.getBasePlan()); saveppiDO.add(ppiDO); proxyFl = true; }
fuente
Su clase Custom AuthenticationProvider debe estar anotada con lo siguiente:
Esto asegurará la presencia de la sesión de hibernación allí también.
fuente
Para aquellos que tienen este problema con la recopilación de enumeraciones, aquí se explica cómo resolverlo:
@Enumerated(EnumType.STRING) @Column(name = "OPTION") @CollectionTable(name = "MY_ENTITY_MY_OPTION") @ElementCollection(targetClass = MyOptionEnum.class, fetch = EAGER) Collection<MyOptionEnum> options;
fuente
En primer lugar, me gustaría decir que todos los usuarios que dijeron sobre pereza y transacciones tenían razón. Pero en mi caso hubo una ligera diferencia en que utilicé el resultado del método @Transactional en una prueba y eso estaba fuera de la transacción real, así que obtuve esta excepción perezosa.
Mi método de servicio:
@Transactional User get(String uid) {};
Mi código de prueba:
User user = userService.get("123"); user.getActors(); //org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role
Mi solución a esto fue envolver ese código en otra transacción como esta:
List<Actor> actors = new ArrayList<>(); transactionTemplate.execute((status) -> actors.addAll(userService.get("123").getActors()));
fuente
Creo que en lugar de habilitar la búsqueda ansiosa, tiene sentido reinicializar su entidad donde sea necesario para evitar una
LazyInitializationException
excepciónfuente
Para aquellos que usan JaVers , dada una clase de entidad auditada, es posible que desee ignorar las propiedades que causan la
LazyInitializationException
excepción (por ejemplo, utilizando la@DiffIgnore
anotación).Esto le dice al marco que ignore esas propiedades al calcular las diferencias de objeto, por lo que no intentará leer desde la base de datos los objetos relacionados fuera del alcance de la transacción (lo que provoca la excepción).
fuente
Una práctica común es poner un valor por
@Transactional
encima de su clase de servicio.@Service @Transactional public class MyServiceImpl implements MyService{ ... }
fuente
Agregar la anotación
@JsonManagedReference
Por ejemplo:
@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; }
fuente