DAO pattern - where do transactions fit in?

前端 未结 5 1488
独厮守ぢ
独厮守ぢ 2021-02-05 04:54

So I\'ve got this generic DAO thing going on and at face value it appears to be ok. It\'s basically modeled after the CaveatEmptor sample application from the Hibernate guys.

相关标签:
5条回答
  • 2021-02-05 05:38

    In the past I've put the transaction logic in the root DAO for a hierarchy of DAOs that match to a hierarchy of Objects in your model that represent a single solid entity in the system.

    I.e., if you have and X that has many Ys, and you want to store and retrieve Xs and their Ys at the same time as a single compound object, then your DAO for X should also call the DAO for Y. Then you can put a transaction around everything in your add() and update() methods in the DAO for X - and even make the Y DAO package private to hide it from your main business logic. I.e., instead of business logic:

    XDAO xDAO = new XDAO(conn);
    xDAO.startTransaction();
    boolean success = xDAO.add(x);
    if (success)
        for (Y y : x.getYs()) {
            success = YDAO.add(y);
            if (!success) break;
        }
    if (success)
        xDAO.commit();
    else
        xDAO.rollback();
    

    You would just have:

    XDAO xDAO = new XDAO(conn);
    xDAO.add(x);
    

    (with the success / commit / rollback logic internal to that DAO)

    However this doesn't cover every situation, and yours may be different (for example mine works with JDBC, I don't know how Hibernate works or if it is possible there).

    0 讨论(0)
  • 2021-02-05 05:40

    Your right that the application is a good place to cordinate transactions as this allows composition of more complex actions implemented by various services / managers / or whatever you want to call them.

    An easy solution is to define an ITransaction interface, and use some type of factory or DI to hide the actual ITransaction implementor from your application. I rolled my own like this in .net using nHibernate and essentially I have a base class that all my managers (A manager in this case contains business logic for a logical set of entities like Membership, Order which might use one or more repositories). My base class has a ITransaction BeginTransaction(), which dynamically creates a type based on a configuration file.

    This class then works with nHibernate's Session to begin and commit transactions.

    0 讨论(0)
  • 2021-02-05 05:43

    Maybe this is a bit too late for an answer, but how about creating another class for specific transactions, which sits between the business layer and the dao layer? E.g. if methods a() and b() from a DAO are to be run in a transaction for some specific foo() business method, then create something like fooInTransaction() that starts a transaction and calls a() and b() in it. The business method foo() delegates to it.

    This will keep the business code clean and duplication can be avoided by re-factoring.

    0 讨论(0)
  • 2021-02-05 05:52

    I remember that Martin Fowler advices to keep the control of the transaction in the business layer because transaction is a business problem. (If you design a BankAccount class, a transaction is part of the domain language).

    You can try to implement a TransactionScope as in .NET it works something like that

    using (TransactionScope ts = new TransactionScope())
    {
      ...
    }
    

    It's the same thing as (not exactly but if you are a Java guy, it's more explicit to you)

    TransactionScope scope = new TransactionScope();
    try
    {
     ...
     scope.Commit();
    }
    catch(Exception ex)
    {
      scope.Rollback();
      throw;
    }
    

    To decouple your business layer from any DAO technologies you can add a TransactionFactory in your domain language, which return a ITransactionScope (an interface) that you have defined with a Commit and Rollback methods. This way your domain layer is not bound to your DAO layer, only a concrete implementation of TransactionFactory is.

    ITransactionScope scope = transactionFactory.CreateTransaction();
    try
    {
     ...
     scope.Commit();
    }
    catch(Exception ex)
    {
      scope.Rollback();
      throw;
    }
    
    0 讨论(0)
  • 2021-02-05 05:52

    In a web app, what I do to demarcate transactions is to take advantage of the HTTP request/response cycle, where each atomic business operation executes in the scope of one of these cycles, in a single dedicated thread.

    Whatever web framework is used (Struts, JSF, GWT, etc.), there typically exists a "seam" where transaction demarcation can be performed. In Struts, it can be a base Action class. In GWT, it can be a base RemoteServiceImpl class.

    So, use that central point of access to open the transaction (before allowing the application-specific code to execute), and to terminate it with a commit if no exceptions bubbled up or a rollback otherwise (after the application-specific code was executed).

    I applied this strategy extensively in a large and complex business web app, and it proved to work very well.

    0 讨论(0)
提交回复
热议问题