Tengo una clase de persona:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
// etc
}
Con una relación de muchos a muchos que es vago.
En mi controlador tengo
@Controller
@RequestMapping("/person")
public class PersonController {
@Autowired
PersonRepository personRepository;
@RequestMapping("/get")
public @ResponseBody Person getPerson() {
Person person = personRepository.findOne(1L);
return person;
}
}
Y el PersonRepository es solo este código, escrito de acuerdo con esta guía
public interface PersonRepository extends JpaRepository<Person, Long> {
}
Sin embargo, en este controlador realmente necesito los datos perezosos. ¿Cómo puedo activar su carga?
Intentar acceder fallará con
no se pudo inicializar perezosamente una colección de roles: no.dusken.momus.model.Person.roles, no se pudo inicializar el proxy: sin sesión
u otras excepciones según lo que intente.
Mi descripción xml , en caso de que sea necesario.
Gracias.
Person
objeto dado algún parámetro? En esoQuery
, incluya lafetch
cláusula y cargueRoles
también para la persona.Respuestas:
Tendrá que hacer una llamada explícita a la colección perezosa para inicializarla (la práctica común es llamar
.size()
para este propósito). En Hibernate hay un método dedicado para esto (Hibernate.initialize()
), pero JPA no tiene equivalente. Por supuesto, tendrá que asegurarse de que se realiza la invocación, cuando la sesión todavía está disponible, así que anote su método de controlador con@Transactional
. Una alternativa es crear una capa de Servicio intermedia entre el Controlador y el Repositorio que podría exponer métodos que inicializan colecciones diferidas.Actualizar:
Tenga en cuenta que la solución anterior es fácil, pero da como resultado dos consultas distintas a la base de datos (una para el usuario y otra para sus funciones). Si desea lograr un mejor rendimiento, agregue el siguiente método a su interfaz de repositorio Spring Data JPA:
Este método utilizará la cláusula fetch join de JPQL para cargar con entusiasmo la asociación de roles en un solo viaje de ida y vuelta a la base de datos y, por lo tanto, mitigará la penalización de rendimiento incurrida por las dos consultas distintas en la solución anterior.
fuente
join
sin elfetch
, el conjunto será devuelto coninitialized = false
; por lo tanto, sigue emitiendo una segunda consulta una vez que se accede al conjunto.fetch
es clave para asegurarse de que la relación esté completamente cargada y evitar la segunda consulta.Aunque esta es una publicación antigua, considere usar @NamedEntityGraph (Javax Persistence) y @EntityGraph (Spring Data JPA). La combinación funciona.
Ejemplo
y luego el repositorio de primavera como a continuación
fuente
@NamedEntityGraph
es parte de la API JPA 2.1, que no está implementada en Hibernate antes de la versión 4.3.0.@EntityGraph(attributePaths = "employeeGroups")
puede usarse directamente en un repositorio de datos de Spring para anotar un método sin la necesidad de un@NamedEntityGraph
código sin @Entity, fácil de entender cuando abre el repositorio.Tienes algunas opciones
Más trabajo, mejor rendimiento.
Menos trabajo, generalmente aceptable en entornos web.
Menos trabajo, útil cuando OEMIV no está en opción, por ejemplo en una aplicación Swing, pero también puede ser útil en implementaciones de repositorio para inicializar cualquier entidad de una sola vez.
Para la última opción, escribí una clase de utilidad, JpaUtils para inicializar entidades en alguna definición.
Por ejemplo:
fuente
solo se puede cargar perezosamente mientras está dentro de una transacción. Por lo tanto, puede acceder a la colección en su repositorio, que tiene una transacción, o lo que normalmente hago es un
get with association
, o establecer fetchmode en ansioso.fuente
Creo que necesita OpenSessionInViewFilter para mantener su sesión abierta durante el renderizado de vistas (pero no es una buena práctica).
fuente
Datos de primavera
JpaRepository
Spring Data
JpaRepository
define los siguientes dos métodos:getOne
, que devuelve un proxy de entidad adecuado para establecer un@ManyToOne
@OneToOne
asociación principal o principal cuando persiste una entidad secundaria .findById
, que devuelve la entidad POJO después de ejecutar la instrucción SELECT que carga la entidad desde la tabla asociadaSin embargo, en su caso, que no llama a cualquiera
getOne
ofindById
:Entonces, supongo que el
findOne
método es un método que definiste en elPersonRepository
. Sin embargo, elfindOne
método no es muy útil en su caso. Como debe buscar la colecciónPerson
junto con isroles
, es mejor usar unfindOneWithRoles
método.Métodos personalizados de Spring Data
Puede definir una
PersonRepositoryCustom
interfaz de la siguiente manera:Y defina su implementación de esta manera:
¡Eso es!
fuente
Puedes hacer lo mismo así:
Simplemente use faqQuestions.getFaqAnswers (). Size () en su controlador y obtendrá el tamaño de la lista perezosamente inicializada, sin obtener la lista en sí.
fuente