Transactions dans Spring

Transactions SQL dans un projet Spring

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

Certaines opérations sur la base de données peuvent être très complexes. Les objets y participant peuvent dépendre les uns les autres. Pour la cohérence des informations, il est donc important de respecter au maximum l'atomicité des données. Il faut alors privilégier le principe "all or nothing", où soit on effectue toutes les opérations, soit aucune. Transactions sont une technique idéale pour son implémentation.

A travers cet article on verra deux façons de gérer les transactions dans Spring. La première méthode sera basée sur l'injection d'un gestionnaire des transactions à la couche service. L'autre méthode, peu utilisée dans notre exemple d'application Spring, se reposera sur l'annotation @Transactional.

Gestion des transactions dans Spring

Les transactions peuvent être gérées dans un fichier XML, via l'annotation @Transactional ou explicitement dans le code avec l'implémentation de l'interface org.springframework.transaction.PlatformTransactionManager défini dans la liste des beans. Dans notre cas il s'agit du JpaTransactionManager.

@Service("penaltyService")
public class PenaltyServiceImpl implements PenaltyService {

    @Autowired
    private PlatformTransactionManager transactionManager;
    // other attributes are ommitted

    public List<Penalty> saveNotValid(PenaltyForm penaltyForm) throws Exception {
        List<Penalty> penalties = new ArrayList<Penalty>();
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            PaymentMethod paymentMethod = paymentMethodRepository.findOne(penaltyForm.getModeChecked());
            for (Penalty penalty : penaltyForm.getPenaltiesChecked()) {
                penalty.setState(Penalty.STATE_PENDING);
                penalty.setPaymentMethod(paymentMethod);
                penalty = penaltyRepository.save(penalty);
                penalties.add(penalty);
            }
            transactionManager.commit(status);
        } catch(Exception e) {
            logger.error("An exception occured on saving PenaltyRepository", e);
            penalties = null;
            transactionManager.rollback(status);
            throw new Exception(e);
        }
        return penalties;
    }
}

Au tout début on récupère l'instance du bean transactionManager avec l'annotation @Autowired. D'abord, on crée l'instance de la classe DefaultTransactionDefinition. Il s'agit de la transaction et de toutes ses caractéristiques (readOnly, PROPAGATION_REQUIRED, ISOLATION_DEFAULT ou TIMEOUT_DEFAULT). Grâce à cette définition on crée l'instance de la classe TransactionStatus. Ce statut sera utilisé plus loin pour commiter (transactionManager.commit()) ou annuler toutes les requêtes (transactionManager.rollback).

TODO : exemple et explication d'un @Transactional(readOnly = true)

Bartosz KONIECZNY Couche des données

Une question ? Une remarque ?

*

*

Un conseil Symfony2

Comment personnaliser l'affichage des champs du formulaire ?

Pour personnaliser l'affichage des champs du formulaire sous Symfony2 on doit surcharger le paramètre nommé templating.helper.form.class. Par défaut il s'agit de la classe Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper. Le code se présente ainsi :

<?xml version="1.0" encoding="UTF-8" ?>
  <container xmlns="http://symfony.com/schema/dic/services"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <parameters>
      <parameter key="templating.helper.form.class">Frontend\FrontBundle\Helper\FormHelper
    </parameters>
  </container>
La classe surchargée va, avant d'afficher le champ, faire appel à la méthode setTheme(). Tout cela pour déterminer quel thème doit utiliser Symfony2 pour le rendu du formulaire (champ text, boutons radio etc.). Le code de cette FormHelper se présente ainsi :
namespace Frontend\FrontBundle\Helper;

use Symfony\Component\Templating\Helper\Helper;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Util\FormUtil;
use Symfony\Bundle\FrameworkBundle\Templating\Helper\FormHelper as ParentHelper;

class FormHelper extends ParentHelper
{
    protected $engine;

    protected $varStack;

    protected $context;

    protected $resources;

    protected $themes;

    protected $templates;

    protected function renderSection(FormView $view, $section, array $variables = array())
    {
        // if new theme is set (for exemple for override form templates)
        if(isset($variables['attr']['theme'])) $this->setTheme($view,  $variables['attr']['theme']);
        return parent::renderSection($view, $section, $variables);
    }

}
Afin de personnaliser un champ du formulaire sous Symfony2 il faut passer un paramètre theme qui appelera la fonction setTheme. Voici un exemple :
// it will show the template located ad /app/Resources/views directory
echo $view['form']->widget($form['replyType'], array('attr' => array('theme' => array(0 => ':'))));
Pour voir comment on peut personnaliser ces champs, veuillez vous référer aux fichiers placés dans /vendor/Symfony/Bundle/FrameworkBundle/Resources/views/Form.