Transaction pour les POJO

J'implémente une méthode qui fait quelque chose comme:

...
try {
   myPojo.setProperty("foo");
   myService.execute(myPojo);
} catch (Exception e) {
   logger.error(e.getMessage(), e);
}
...

Si une exception est levée par mon service à partir de ce bloc try sur la propriété pojo, la nouvelle valeur apparaîtra. Existe-t-il un moyen de lancer un type de transaction pour les changements de pojo et de le renvoyer si quelque chose ne va pas?

Quelque chose comme:

PojoTransaction pt = startPojoTransaction();
transactionedPojo = pt.handleByTransaction(myPojo);
try {
   transactionedPojo.setProperty("foo");
   myService.execute(transactionedPojo);
   pt.commit;
} catch (Exception e) {
   logger.error(e.getMessage(), e);
}

Ou quelque chose de similaire ...

0

3 Réponses

Take a look at the Memento Pattern, it includes a Java example.
http://en.wikipedia.org/wiki/Memento_pattern

0
ajouté

La question est un peu vague, mais il semble que vous luttiez avec le modèle de conception de base pour la gestion des transactions. Vous bénéficieriez grandement de l'expérience acquise dans la production du motif utilisé ici:

http://static.springsource.org /spring/docs/3.0.x/spring-framework-reference/html/transaction.html

Peut-être que la gestion de Spring Transaction vous conviendrait parfaitement pour votre projet.

0
ajouté

J'ai joué avec l'idée, c'est loin d'être parfait, juste une simple preuve de concept. Il y a des pièges dans cette implémentation:

  • Il essaie seulement d'appeler un constructeur sans paramètre de la source donnée objet pour créer la copie cible, aurait besoin d'une certaine logique pour sélectionner un constructeur correct (ou ne supporte que Cloneables?)
  • Copie uniquement les champs déclarés dans la classe, pas ceux des superclasses (ce problème peut être résolu en parcourant l'arbre d'héritage et en copiant les champs de la superclasse)
  • Si les champs sont des types complexes, seules les références sont copiées dans target-object, donc aucune modification ne sera transactionnelle, car la source et la cible partagent la même instance (résoluble en créant récursivement des copies d'objets imbriqués et copier leurs valeurs, nécessite de parcourir tout le graphe d'objets, en partant de la source, puis de le faire vice-versa à l'instant de la validation)

Mais, en améliorant d'ici, je crois que cela pourrait devenir très utilisable. Voici le POC:

import java.lang.reflect.Field;

import org.junit.Assert;
import org.junit.Test;

public class PojoTransactionTest
{
    public static class PojoTransaction
    {
        /**
         * This is the original (unmodified) object
         */
        private T source;

        /**
         * This is the object modified by within the transaction
         */
        private T target;

        /**
         * Creates a new transaction for the given source object
         * @param source    Source object to modify transactionally
         */
        public PojoTransaction(T source)
        {
            try
            {
                this.source = source;
                this.target = (T)source.getClass().newInstance(); //Note: this only supports parameterless constructors

                copyState(source, target);
            }
            catch(Exception e)
            {
                throw new RuntimeException("Failed to create PojoTransaction", e);
            }
        }

        /**
         * Copies state (member fields) from object to another
         * @param from      Object to copy from
         * @param to        Object to copy to
         * @throws IllegalAccessException
         */
        private void copyState(T from, T to) throws IllegalAccessException
        {
            //Copy internal state to target, note that this will NOT copy fields from superclasses
            for(Field f : from.getClass().getDeclaredFields())
            {
                f.setAccessible(true);
                f.set(to, f.get(from));
            }
        }

        /**
         * Returns the transaction target object, this is the one you should modify during transaction
         * @return Target object
         */
        public T getTransactionTarget()
        {
            return target;
        }

        /**
         * Copies the changes from target object back to original object
         */
        public void commit()
        {
            try
            {
                copyState(target, source);
            }
            catch(Exception e)
            {
                throw new RuntimeException("Failed to change state of original object", e);
            }
        }
    }

    public static class TestData
    {
        private String strValue = "TEST";
        private int intValue = 1;
        private float floatValue = 3.1415f;

        public String getStrValue()
        {
            return strValue;
        }

        public void setStrValue(String strValue)
        {
            this.strValue = strValue;
        }

        public int getIntValue()
        {
            return intValue;
        }

        public void setIntValue(int intValue)
        {
            this.intValue = intValue;
        }

        public float getFloatValue()
        {
            return floatValue;
        }

        public void setFloatValue(float floatValue)
        {
            this.floatValue = floatValue;
        }

    }

    @Test
    public void testTransaction()
    {
        //Create some test data
        TestData orig = new TestData();

        //Create transaction for the test data, get the "transaction target"-object from transaction
        PojoTransaction tx = new PojoTransaction(orig);
        TestData target = tx.getTransactionTarget();
        target.setFloatValue(1.0f);
        target.setIntValue(5);
        target.setStrValue("Another string");

        //Original object is still at the original values
        Assert.assertEquals(1, orig.getIntValue());
        Assert.assertEquals(3.1415f, orig.getFloatValue(), 0.001f);
        Assert.assertEquals("TEST", orig.getStrValue());

        //Commit transaction
        tx.commit();

        //The "orig"-object should now have the changes made to "transaction target"-object
        Assert.assertEquals(5, orig.getIntValue());
        Assert.assertEquals(1.0f, orig.getFloatValue(), 0.001f);
        Assert.assertEquals("Another string", orig.getStrValue());
    }

}
0
ajouté