2012-06-30 21 views
11

¿Hay alguna solución simple para guardar datos en la base de datos usando JPA en un nuevo hilo?Llamando a los métodos @Transactional desde otro hilo (Ejecutable)

La aplicación web My Spring permite al usuario administrar tareas programadas. En tiempo de ejecución, puede crear e iniciar nuevas instancias de tareas predefinidas. Estoy usando TaskScheduler de primavera y todo funciona bien.

Pero necesito guardar el resultado booleano de cada tarea disparada en la base de datos. ¿Cómo puedo hacer esto?

EDIT: Tengo que generalizar mi pregunta: Necesito llamar a un método en mi clase @Service desde las tareas. Porque el resultado de la tarea debe ser "procesado" antes de guardarlo en la base de datos.

EDIT 2: La versión simplificada de mi código problemático viene aquí. Cuando se llama a saveTaskResult() desde el programador, el mensaje se imprime pero no se guarda nada en db. Pero cada vez que llamo a saveTaskResult() desde el controlador, el registro se guarda correctamente en la base de datos.

@Service 
public class DemoService { 

    @Autowired 
    private TaskResultDao taskResultDao; 

    @Autowired 
    private TaskScheduler scheduler; 

    public void scheduleNewTask() { 
     scheduler.scheduleWithFixedDelay(new Runnable() { 

      public void run() { 
       // do some action here 
       saveTaskResult(new TaskResult("result")); 
      } 

     }, 1000L); 
    } 

    @Transactional 
    public void saveTaskResult(TaskResult result) { 
     System.out.println("saving task result"); 
     taskResultDao.persist(result); 
    } 

} 
+0

¿Cuál es exactamente el problema? Inicia un hilo, llama a sus servicios de Spring exactamente como lo hubiera hecho si no hubiera iniciado un hilo, y todo debería estar bien. –

+0

El problema es que el método de negocio objetivo es @Transactional. Cuando llamo a este método en run(), los datos no se conservan. (He actualizado el título de la pregunta) –

+0

Al interceptor transaccional no le importa si el hilo que llama al método es un hilo creado por su contenedor de servlets, o un hilo creado por usted. Deberia de funcionar. Muéstranos un código. –

Respuesta

14

El problema con su código es que espera que se inicie una transacción cuando llama al saveTaskResult(). Esto no sucederá porque Spring usa AOP para iniciar y detener transacciones.

Si obtiene una instancia de un bean de primavera transaccional desde la fábrica de beans, o mediante inyección de dependencia, lo que obtiene es, de hecho, un proxy alrededor del bean. Este proxy inicia una transacción antes de llamar al método real, y confirma o revierte la transacción una vez que el método se ha completado.

En este caso, llama a un método local del bean, sin pasar por el proxy transaccional. Ponga el método saveTaskResult() (anotado con @Transactional) en otro Spring Bean. Inyecte este otro bean de Spring en DemoService, y llame al otro Spring Bean del DemoService, y todo estará bien.

+0

Genial, funciona. Esto es exactamente lo que necesitaba saber. Muchas gracias –

+0

Gracias un millón. esta solución funciona con CompletableFuture like charm. –

2

Las transacciones se llevan a cabo en el almacenamiento local de subprocesos.
Si su otro método ejecuta un hilo con la anotación @Transactional.
El valor predeterminado es REQUIRED y esto significa que si ejecuta un método anotado con @Transacitonal desde un subproceso diferente, tendrá una nueva transacción (ya que no hay transacción retenida en el almacenamiento local de subproceso de este subproceso).

Cuestiones relacionadas