web-dev-qa-db-fra.com

Quelques précisions sur l'annotation Spring @Transactional sur une méthode

Je suis assez nouveau dans Spring World et j'ai développé un projet simple qui utilise Spring 3.2.1 et Hibernate 4.1.9 pour implémenter un DAO. Le projet fonctionne correctement mais j'ai quelques doutes sur l'utilisation de @ Transactional Annotation Spring sur la méthode CRUD de ce DAO.

Voici le code complet de la classe qui implémente l'opération CRUD de mon projet:

package org.andrea.myexample.HibernateOnSpring.dao;

import Java.util.List;

import org.andrea.myexample.HibernateOnSpring.entity.Person;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.springframework.transaction.annotation.Transactional;

public class PersonDAOImpl implements PersonDAO {

    // Factory per la creazione delle sessioni di Hibernate:
    private static SessionFactory sessionFactory;

    // Metodo Setter per l'iniezione della dipendenza della SessionFactory:
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /** CREATE CRUD Operation:
     * Aggiunge un nuovo record rappresentato nella tabella rappresentato
     * da un oggetto Person
     */
    @Transactional(readOnly = false)
    public Integer addPerson(Person p) {

        System.out.println("Inside addPerson()");

        Session session = sessionFactory.openSession();

        Transaction tx = null;
        Integer personID = null;

        try {
            tx = session.beginTransaction();

            personID = (Integer) session.save(p);
            tx.commit();
        } catch (HibernateException e) {
            if (tx != null)
                tx.rollback();
            e.printStackTrace();
        } finally {
            session.close();
        }

        return personID;

    }

    // READ CRUD Operation (legge un singolo record avente uno specifico id):
    public Person getById(int id) {

        System.out.println("Inside getById()");

        Session session = sessionFactory.openSession();

        Transaction tx = null;          
        Person retrievedPerson = null;  

        try {
            tx = session.beginTransaction();
            retrievedPerson = (Person) session.get(Person.class, id);
            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {                 
            session.close();
        }

        return retrievedPerson;
    }

    // READ CRUD Operation (recupera la lista di tutti i record nella tabella):
    @SuppressWarnings("unchecked")
    public List<Person> getPersonsList() {

        System.out.println("Inside getPersonsList()");

        Session session = sessionFactory.openSession();
        Transaction tx = null;
        List<Person> personList = null;

        try {
            tx = session.beginTransaction();
            Criteria criteria = session.createCriteria(Person.class);
            personList = criteria.list();
            System.out.println("personList: " + personList);
            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {
            session.close();
        }
        return personList;
    }

    // DELETE CRUD Operation (elimina un singolo record avente uno specifico id):
    public void delete(int id) {

        System.out.println("Inside delete()");

        Session session = sessionFactory.openSession();
        Transaction tx = null;

        try {
            tx = session.beginTransaction();
            Person personToDelete = getById(id);
            session.delete(personToDelete);
            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {
            session.close();
        }

    }

    @Transactional
    public void update(Person personToUpdate) {

        System.out.println("Inside update()");

        Session session = sessionFactory.openSession();
        Transaction tx = null;

        try {
            System.out.println("Insite update() method try");
            tx = session.beginTransaction();
            session.update(personToUpdate);

            tx.commit();
        }catch (HibernateException e) { 
            if (tx != null)                 
                tx.rollback();          
            e.printStackTrace();
        } finally {
            session.close();
        }   

    }

}

Ok, comme vous pouvez le voir, certaines méthodes sont annotées en utilisant l'annotation @Transactional.

Je lis la documentation officielle ici http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/transaction.html sur l'utilisation de cette annotation sur méthodes et il voit que: Une méthode annotée en utilisant @Transactional doit avoir une sémantique transactionnelle mais qu'est-ce que cela signifie avec la sémantique transactionnelle?

Cela signifie que l'exécution du methos doit être considérée comme l'exécution d'une transaction? Cela signifie donc que les opérations de la méthode doivent être considérées comme une seule opération qui peut conduire à un succès ou à un échec.Si elles réussissent, les résultats des opérations doivent être permanents, alors qu'en cas de non-retour à l'état avant le début de la transaction.

Est-ce le sens de l'utilisation @ Annotation transactionnelle sur une méthode?

Et que signifie exactement l'attribut readOnly = false dans l'annotation @Transactional de addPerson () méthode? cela signifie que je peux également écrire un enregistrement dans la base de données (et pas seulement le lire) ou quoi? Le doute est lié car j'ai compris que, par défaut, une transaction définie à l'aide de l'annotation @Transactional est en lecture/écriture et pas seulement en lecture ... I ont également essayé de supprimer l'attribut (readOnly = false) et fonctionnent toujours bien (insérez le nouvel enregistrement dans la table de base de données)

Le point suivant est: "pourquoi certaines méthodes sont annotées en utilisant l'annotation @Transactional et d'autres méthodes non? Est-ce une bonne pratique d'annoter TOUTES les méthodes CRUD avec @Transactional?"

Tnx

Andrea

38
AndreaNobili

Tout d'abord, vous ne devez pas rendre les méthodes DAO transactionnelles, mais les méthodes de service.

Deuxièmement, l'utilisation de Transactional est un moyen de laisser Spring démarrer et valider/annuler des transactions pour vous. Vous ne devez donc pas démarrer et valider des transactions vous-même.

Troisièmement: cela ne fonctionnera que si vous utilisez un gestionnaire de transactions qui sait associer une session Hibernate à la transaction (généralement, un HibernateTransactionManager). La fabrique de sessions doit également être gérée par Spring et injectée par Spring dans vos DAO. Le code du DAO devrait ressembler à ceci:

Quatrièmement: vous ne devez pas ouvrir une nouvelle session, mais obtenir la session actuelle, associée à la transaction en cours d'ici le printemps.

public class PersonDAOImpl implements PersonDAO {

    @Autowired
    private SessionFactory sessionFactory;

    public Integer addPerson(Person p) {
        Session session = sessionFactory.getCurrentSession();
        Integer personID = (Integer) session.save(p);
        return personID;
    }

    public Person getById(int id) {
        Session session = sessionFactory.getCurrentSession();
        Person retrievedPerson = (Person) session.get(Person.class, id);
        return retrievedPerson;
    }

    @SuppressWarnings("unchecked")
    public List<Person> getPersonsList() {
        Session session = sessionFactory.getCurrentSession();
        Criteria criteria = session.createCriteria(Person.class);
        return criteria.list();
    }

    public void delete(int id) {
        Session session = sessionFactory.getCurrentSession();
        Person personToDelete = getById(id);
        session.delete(personToDelete);
    }

    public void update(Person personToUpdate) {
        Session session = sessionFactory.getCurrentSession();
        session.update(personToUpdate);
    }
}

Lisez la documentation pour plus d'informations.

71
JB Nizet

@Transactional est utilisé sur la méthode.

Nous déclarons au niveau de la méthode d'abord qu'il ouvre la transaction, exécute l'opération et ferme la transaction.

Si une opération échoue, elle sera annulée, si une opération réussit, elle sera automatiquement validée

C'est à propos de @Transactional annotation enfin & courte.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        ">

    <!-- Scans the classpath for annotated components that will be auto-registered as Spring beans -->
    <context:component-scan base-package="hu.daniel.hari.learn.spring" />
    <!-- Activates various annotations to be detected in bean classes e.g: @Autowired -->
    <context:annotation-config />


    <!-- creating the internal datasource object -->

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
        <property name="url" value="jdbc:hsqldb:mem://productDb" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
            p:packagesToScan="hu.daniel.hari.learn.spring.orm.model"
            p:dataSource-ref="dataSource"
            >
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="true" />
                <property name="showSql" value="true" />

            </bean>
        </property>
    </bean>

    <!-- Transactions -->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>
package hu.daniel.hari.learn.spring.orm.main;

import hu.daniel.hari.learn.spring.orm.model.Product;
import hu.daniel.hari.learn.spring.orm.service.ProductService;

import Java.util.Arrays;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;

public class SpringOrmMain {

    public static void main(String[] args) {

        //Create Spring application context
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");

        //Get service from context. (service's dependency (ProductDAO) is autowired in ProductService)
        ProductService productService = ctx.getBean(ProductService.class);

        //Do some data operation

        productService.add(new Product(1, "Bulb"));
        productService.add(new Product(2, "Dijone mustard"));

        System.out.println("listAll: " + productService.listAll());

        //Test transaction rollback (duplicated key)

        try {
            productService.addAll(Arrays.asList(new Product(3, "Book"), new Product(4, "Soap"), new Product(1, "Computer")));
        } catch (DataAccessException dataAccessException) {
        }

        //Test element list after rollback
        System.out.println("listAll: " + productService.listAll());

        ctx.close();
    }
}
3