首页
学习
活动
专区
圈层
工具
发布

POJO事务
EN

Stack Overflow用户
提问于 2012-05-24 15:06:18
回答 3查看 1.5K关注 0票数 1

我正在实现一个方法,它的功能类似于:

代码语言:javascript
复制
...
try {
   myPojo.setProperty("foo");
   myService.execute(myPojo);
} catch (Exception e) {
   logger.error(e.getMessage(), e);
}
...

如果我的服务从这个try块抛出了一些异常,那么pojo属性上的try块将具有新值。有没有办法为pojo更改启动某种事务,并在出现错误时回滚它?

类似于:

代码语言:javascript
复制
PojoTransaction pt = startPojoTransaction();
transactionedPojo = pt.handleByTransaction(myPojo);
try {
   transactionedPojo.setProperty("foo");
   myService.execute(transactionedPojo);
   pt.commit;
} catch (Exception e) {
   logger.error(e.getMessage(), e);
}

或者类似的东西。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-05-24 15:49:09

我玩弄着这个想法,这远远不是完美的,只是一个简单的概念证明。在这个实现中有一些陷阱:

  • 它只尝试调用给定源对象的无参数构造函数来创建目标副本,将需要一些逻辑来选择正确的构造函数(或者只支持在类中声明的Cloneables?)
  • Only复制字段,而不是从超类中复制(这个问题可以通过遍历继承树并复制任何超类字段来解决)
  • 如果字段是复杂类型,只有引用被复制到目标对象,所以对它们的任何更改都不是事务性的,因为源和目标共享相同的实例(可以通过递归地创建嵌套对象的副本并复制它们的值来解决,需要遍历整个对象图,从源开始,然后在提交时反之亦然)

但是,从这里开始改进,我相信它会变得非常有用。以下是POC:

代码语言:javascript
复制
import java.lang.reflect.Field;

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

public class PojoTransactionTest
{
    public static class PojoTransaction<T>
    {
        /**
         * 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<TestData> tx = new PojoTransaction<TestData>(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());
    }

}
票数 1
EN

Stack Overflow用户

发布于 2012-05-24 15:13:46

看看Memento模式,它包含一个Java示例。

http://en.wikipedia.org/wiki/Memento_pattern

票数 2
EN

Stack Overflow用户

发布于 2012-05-24 15:14:27

这个问题有点模糊,但听起来您似乎正在与事务管理的基本设计模式搏斗。您将从这里使用的模式的生产过程中获得极大的好处:

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

不管怎样,Spring事务管理可能非常适合您的项目。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10732726

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档