mvc framework example español ejemplo data arquitectura spring jpa java-ee spring-data spring-data-jpa

framework - spring mvc español



¿Cómo se usa Spring Data JPA fuera de Spring Container? (2)

Estoy tratando de conectar manualmente los objetos JPA de Spring Data para que pueda generar proxies DAO (también conocidos como Repositorios), sin utilizar un contenedor Spring bean.

Inevitablemente, se me preguntará por qué quiero hacer esto: es porque nuestro proyecto ya está usando Google Guice (y en la interfaz de usuario usando Gin con GWT), y no queremos mantener otra configuración de contenedor IoC, ni acceder a ella. todas las dependencias resultantes. Sé que podríamos usar la SpringIntegration de SpringIntegration de Guice, pero este sería el último recurso.

Parece que todo está disponible para cablear los objetos manualmente, pero como no está bien documentado, estoy teniendo un momento difícil.

De acuerdo con la guía del usuario de Spring Data, es posible usar fábricas de repositorios independientes . Desafortunadamente, el ejemplo muestra RepositoryFactorySupport que es una clase abstracta. Después de buscar, logré encontrar JpaRepositoryFactory

JpaRepositoryFactory realidad funciona bastante bien, excepto que no crea automáticamente transacciones. Las transacciones se deben administrar manualmente, o no se mantendrá nada en la base de datos:

entityManager.getTransaction().begin(); repositoryInstance.save(someJpaObject); entityManager.getTransaction().commit();

El problema resultó ser que @Transactional anotaciones @Transactional no se usan automáticamente y necesitan la ayuda de un TransactionInterceptor

Afortunadamente, el JpaRepositoryFactory puede tomar una devolución de llamada para agregar más consejos AOP al proxy Repository generado antes de regresar:

final JpaTransactionManager xactManager = new JpaTransactionManager(emf); final JpaRepositoryFactory factory = new JpaRepositoryFactory(emf.createEntityManager()); factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() { @Override public void postProcess(ProxyFactory factory) { factory.addAdvice(new TransactionInterceptor(xactManager, new AnnotationTransactionAttributeSource())); } });

Aquí es donde las cosas no funcionan tan bien. Al pasar por el depurador en el código, TransactionInterceptor está efectivamente creando una transacción, pero en el EntityManager equivocado. Spring administra el EntityManager activo al observar el hilo que se está ejecutando actualmente. El TransactionInterceptor hace esto y ve que no hay ningún EntityManager activo vinculado al hilo, y decide crear uno nuevo.

Sin embargo, este nuevo EntityManager no es la misma instancia que se creó y pasó al constructor JpaRepositoryFactory , que requiere un EntityManager . La pregunta es, ¿cómo hago que TransactionInterceptor y JpaRepositoryFactory usen el mismo EntityManager ?

Actualizar:

Mientras escribía esto, descubrí cómo resolver el problema, pero aún puede no ser la solución ideal. Publicaré esta solución como una respuesta separada. Me complacería escuchar cualquier sugerencia sobre una mejor manera de utilizar Spring Data JPA de forma independiente de cómo lo he resuelto.


El principio general detrás del diseño de JpaRepositoryFactory y la integración Spring Spring JpaRepositoryFactory bean es el siguiente:

Estamos asumiendo que ejecuta su aplicación dentro de un entorno administrado de tiempo de ejecución JPA, sin importar cuál.

Esa es la razón por la que confiamos en EntityManager inyectado en lugar de en EntityManagerFactory . Por definición, el EntityManager no es seguro para subprocesos. Por lo tanto, si se tratara directamente con una EntityManagerFactory tendríamos que reescribir todo el código de administración de recursos que un entorno de tiempo de ejecución administrado (como Spring o EJB) le proporcionaría.

Para integrarse con la gestión de transacciones de Spring, SharedEntityManagerCreator de Spring que realmente hace la magia de enlace de recursos de transacciones que ha implementado manualmente. Entonces, probablemente quiera usar ese para crear instancias de EntityManager desde su EntityManagerFactory . Si desea activar directamente la transaccionalidad en los beans del repositorio (para que una llamada a p.ej. repo.save(…) cree una transacción si ninguna está ya activa) eche un vistazo a la implementación de TransactionalRepositoryProxyPostProcessor en Spring Data Commons. Activa las transacciones cuando los repositorios Spring Data se usan directamente (p. Ej., Para repo.save(…) ) y personaliza levemente la búsqueda de configuración de transacción para preferir las interfaces sobre las clases de implementación para permitir que las interfaces del repositorio anulen la configuración de transacción definida en SimpleJpaRepository .


Lo resolví vinculando manualmente EntityManager y EntityManagerFactory al subproceso que se ejecuta, antes de crear repositorios con JpaRepositoryFactory . Esto se logra utilizando el método TransactionSynchronizationManager.bindResource :

emf = Persistence.createEntityManagerFactory("com.foo.model", properties); em = emf.createEntityManager(); // Create your transaction manager and RespositoryFactory final JpaTransactionManager xactManager = new JpaTransactionManager(emf); final JpaRepositoryFactory factory = new JpaRepositoryFactory(em); // Make sure calls to the repository instance are intercepted for annotated transactions factory.addRepositoryProxyPostProcessor(new RepositoryProxyPostProcessor() { @Override public void postProcess(ProxyFactory factory) { factory.addAdvice(new TransactionInterceptor(xactManager, new MatchAlwaysTransactionAttributeSource())); } }); // Create your repository proxy instance FooRepository repository = factory.getRepository(FooRepository.class); // Bind the same EntityManger used to create the Repository to the thread TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em)); try{ repository.save(someInstance); // Done in a transaction using 1 EntityManger } finally { // Make sure to unbind when done with the repository instance TransactionSynchronizationManager.unbindResource(getEntityManagerFactory()); }

Sin embargo, debe haber una mejor manera. Parece extraño que RepositoryFactory se diseñó para usar EnitiyManager lugar de EntityManagerFactory . Esperaría, que primero vería si un EntityManger está vinculado al hilo y luego crea uno nuevo y lo vincula, o usa uno existente.

Básicamente, me gustaría inyectar los servidores proxy del repositorio, y espero que en cada llamada creen internamente un nuevo EntityManager , para que las llamadas sean seguras para hilos.