Announcement Announcement Module
Collapse
No announcement yet.
Domain Object Model and Hibernate detached object Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Domain Object Model and Hibernate detached object

    Hi ,

    Domain object model again ? .......sorry , because l am in chaos now.

    Let's assume that l am designing a library automation system , and l want it to be a domain-driven design (no Anemic Domain Model in my code , http://www.martinfowler.com/bliki/An...mainModel.html) and l had choose Hibernate as my O/R mapper.

    Let focus on three classes - LibraryUser , Borrow and Item in the system.

    Tables Relationship:

    LibraryUser --(1:M)--> Borrow <--(1:1)--> Item [<--(M:1)-- Book]

    LibraryUser.java,

    Code:
    public class LibraryUser implements java.io.Serializable &#123;
    
    	private Long id;
    	private Long version;
    	private String cardKey;
    	private String unifiedKey;
    	private Date admissionDate;
    	private Date expiryDate;
    	private Set borrows = new HashSet&#40;&#41;;
    	private Set reservations = new HashSet&#40;&#41;;
    	private Set libraryUserTypes = new HashSet&#40;&#41;;
            .....
    
    	// get , set
    
    &#125;
    Borrow.java,

    Code:
    public class Borrow  implements java.io.Serializable &#123;
    
    	private Long id;
         	private Long version;
         	private Date borrowDate;
         	private Date dueDate;
         	private Date returnDate;
         	private Date reportlostDate;
         	private Short renewedNo;
         	private LibraryUserType libraryUserType;
         	private Item item;
         	private LibraryUser libraryUser;
    	.....
    
    	// get,set
    
    &#125;
    Item.java,

    Code:
    public class Item  implements java.io.Serializable &#123;
    
         	private Long id;
         	private Long version;
         	private String barcode;
         	private String shelfMark;
         	private Date lastCheckin;
         	private Set borrows = new HashSet&#40;&#41;;
         	private Book book;
         	private ItemDuration itemDuration;
         	private ItemStatus itemStatus;
         	private ItemType itemType;
         	private Location location;
         	.....
    
    	// get,set
    
    &#125;
    We have data (properties) in all three classes now , now l want to add some behaviours in the classes .Since this is a library system , borrow(loan) book action is a routine, so the first behaviour l want to add to the LibraryUser class is borrow method (any objection on this ? Is there any altenative way to place the borrow method if we want the design to be domain-driven?).

    If the above OK , then the method will be something like ,

    Method 1.

    LibraryUser.java,

    Code:
    public class LibraryUser implements java.io.Serializable &#123;
    
    	.....
    
    	public void borrow&#40;Item item,LibraryUserType libraryUserType&#41; &#123;
    
    		Borrow borrow = new Borrow&#40;&#41;;
    		borrow.setLibraryUser&#40;this&#41;;
    		borrow.setLibraryUserType&#40;libraryUserType&#41;;
    		borrow.setItem&#40;item&#41;;
    		borrow.setBorrowDate&#40;new Date&#40;&#41;&#41;;
    		...
    		// The code here suppose to check the this libraryUser can borrow the item or not
    		// l skip it for simple dicussion , let's assume that not checking is required. &#58;&#41;
    		... 
    
    		this.getBorrows&#40;&#41;.add&#40;borrow&#41;;
    	&#125;
    
    &#125;
    then in my controller , l just feed the item , libraryUserType into the argument of the borrow method and save it,

    controllerA,

    Code:
    	libraryUser.borrow&#40;item,libraryUserType&#41;;
    
    	getLibrary&#40;&#41;.storeLibraryUser&#40;libraryUser&#41;;
    this seem to be a natural way for me , because every libraryUser BORROW book, the borrow(method) behaviour should be own by libraryUser ... but if we store libraryUser(and if the libraryUser is detached) to cascade saving borrow this way , it will cause a lot of borrows to update ( http://forum.hibernate.org/viewtopic...ctbeforeupdate ), unless you set select-before-update="true" in class attribute of the borrow.hbm.xml, but this will cause a performance hit.

    Of course , we can do it another way , feed libraryUser, item , libraryUserType into borrow , and save borrow ,

    Method 2.

    ControllerB,

    Code:
    	Borrow borrow = new Borrow&#40;&#41;;
    	borrow.setLibraryUser&#40;libraryUser&#41;;
    	borrow.setLibraryUserType&#40;libraryUserType&#41;;
    	borrow.setItem&#40;item&#41;;
    	borrow.setBorrowDate&#40;new Date&#40;&#41;&#41;;
    	...
    
    	getLibrary&#40;&#41;.storeBorrow&#40;borrow&#41;;
    this method is the most natural way for relational database , saving a borrow , we need only LIBRARY_USER_ID, ITEM_ID and LIBRARY_USER_TYPE_ID , of course , with DUE_DATE , ...etc ,
    it executes only one query ,

    Code:
    insert into BORROWS &#40;VERSION, BORROW_DATE, DUE_DATE, RETURN_DATE, REPORTLOST_DATE, RENEWED_NO, LIBRARY_USER_TYPE_ID, ITEM_ID, USER_ID&#41; values &#40;?, ?, ?, ?, ?, ?, ?, ?, ?&#41;
    Q1. Is this an Anemic Domain Model way of coding (Method 2) ?

    Q2. which is the domain-driven model way ? or l was wrong from the start ..

    moon

  • #2
    It wasnt clear from that what the relationship is between a borrow and a book.

    First thought - encapsulation...

    In your get/set pairs on collections and things other than simple properties are you guarding these items? For example, can a getBorrows() return the actual set or does it return a Collections.unmodifiableSet(borrows) so the calling code can't modify the internal state of your object without your consent?

    Placement of borrow behavior...

    It seems to me that placing the behavior of borrow on a LibraryUser is not the best place. Does your domain have the concept of a repository for books? Clearly you're interested in knowing what books a particular User borrowed but I'd imagine your also very interested in knowing which books against all users are borrowed at one time. In that sense, it seems to me that you have a BookRepository or whatever name you think of. That thing always tracks borrows AND you want this information on a user.

    If you let the repository "control" the borrows and don't cascade the borrows of a user when saving you avoid the performance problem you discussed. The Set of borrows on a user would then be managed by you in memory but an "inverse" mapping if you using Hibernate. (this section is probably really unclear, sorry )

    Have you read Fowlers Patterns of Enterprise Application Architecture or Eric Evans Domain Driven Design ? You might like them.


    Joe

    Comment


    • #3
      Hi joe ,

      It wasnt clear from that what the relationship is between a borrow and a book.
      There is nothing special about Item and Book --> one book can have a lot items (you can buy 5 items of the same title "Harry Potter").

      Book.java,
      Code:
      public class Book implements java.io.Serializable &#123;
      
      	// Fields
      
      	private Long id;
      	private Long version;
      	private String title;
      	private int publishingYear;
      	private String isbn;
      	private String edition;
      	private String description;
      	private String publicationPlace;
      	private String classMark;
      	private String attachment;
      	private Set items = new HashSet&#40;&#41;;
      	private Set authors = new HashSet&#40;&#41;;
      	private Set subjects = new HashSet&#40;&#41;;
      	private Set seriez = new HashSet&#40;&#41;;
      	private Set otherTitles = new HashSet&#40;&#41;;
      	private Set reservations = new HashSet&#40;&#41;;
      	private Note note;
      	private Publisher publisher;
      	.........// get , set
      
      &#125;
      In your get/set pairs on collections and things other than simple properties are you guarding these items? For example, can a getBorrows() return the actual set or does it return a Collections.unmodifiableSet(borrows) so the calling code can't modify the internal state of your object without your consent?
      No , all are the simple get/set.
      It seems to me that placing the behavior of borrow on a LibraryUser is not the best place.
      But l do found an similar example that this is the right place to put borrow behaviour , see Hibernate in Action (HIA), pg 305 , section "Creating "smart" domain models" , you can read through the whole section to get the idea. Below are some code snipplet to show the similarity (this code are from Webwork in Action's(WIA) source code rather than the HIA),

      Item.java,
      Code:
      public class Item implements Serializable, Comparable, Auditable &#123;
      
          private Long id = null;
          private int version;
          private String name;
          private User seller;
          private String description;
          private MonetaryAmount initialPrice;
          private MonetaryAmount reservePrice;
          private Date startDate;
          private Date endDate;
          private Set categorizedItems = new HashSet&#40;&#41;;
          private Collection bids = new ArrayList&#40;&#41;;
          private Bid successfulBid;
          private ItemState state;
          private User approvedBy;
          private Date approvalDatetime;
          private Date created = new Date&#40;&#41;;
          ....
          public Bid placeBid&#40;User bidder, MonetaryAmount bidAmount,
                              Bid currentMaxBid, Bid currentMinBid&#41;
                  throws BusinessException &#123;
      
              // Check highest bid &#40;can also be a different Strategy &#40;pattern&#41;&#41;
              if &#40;currentMaxBid != null && currentMaxBid.getAmount&#40;&#41;.compareTo&#40;bidAmount&#41; > 0&#41; &#123;
                  throw new BusinessException&#40;"Bid too low."&#41;;
              &#125;
      
              // Auction is active
              if &#40;!state.equals&#40;ItemState.ACTIVE&#41;&#41;
                  throw new BusinessException&#40;"Auction is not active yet."&#41;;
      
              // Auction still valid
              if &#40;this.getEndDate&#40;&#41;.before&#40;new Date&#40;&#41;&#41;&#41;
                  throw new BusinessException&#40;"Can't place new bid, auction already ended."&#41;;
      
              // Create new Bid
              Bid newBid = new Bid&#40;bidAmount, this, bidder&#41;;
      
              // Place bid for this Item
              this.addBid&#40;newBid&#41;;
      
              return newBid;
          &#125;
      &#125;
      The placeBid method in Item.java here are similar to my borrow method in LibraryUser.java.
      Bid.java (fro reference),
      Code:
      public class Bid implements Serializable, Comparable &#123;
      
      	private Long id = null;
      	private MonetaryAmount amount;
      	private Item item;
      	private User bidder;
      	private Date created = new Date&#40;&#41;;
                      ...
      &#125;
      Item.hbm.xml (CaveatEmptor),
      Code:
      <class name="Item"  table="ITEM" lazy="true">
      
      ...
      	<!-- Use a standard parent/child relationship for bids. -->
      	<bag    name="bids"
      			cascade="all"
      			inverse="true"
      			order-by="CREATED desc"
      			lazy="true"
      			access="org.hibernate.auction.persistence.DirectSetAccessor">
      		<key>
                  <column name="ITEM_ID" not-null="true"/>
              </key>
      		<one-to-many class="Bid"/>
      	</bag>
      
      </class>
      notice the cascade="all" attribute in the above Item.hbm.xml mapping , it is same to my case.

      The only different are
      1. my borrow method return void , the placeBid method return Bid , the persistance is doing outside of the Item class (may be in the controller , but l found it in ItemTest.java , see below).
      2. my case is using <set> instead of <bag> in the LibraryUser.hbm.xml mapping file.
      Code:
          <!-- bi-directional one-to-many association to Borrow -->
          <set name="borrows"
          	 table="BORROWS"
               lazy="true"
               inverse="true"
      		 cascade="save-update">
      
              <key>
                  <column name="USER_ID" />
              </key>
      
              <one-to-many 
                  class="org.yourschool.library.domain.Borrow"/>
          </set>
      There is a ItemTest.java in the WIA to show item.placeBid(..) ,
      Code:
      ...
      a2.placeBid&#40;userDAO.getUserById&#40;u3.getId&#40;&#41;, false&#41;,newAmount,currentMaxBid,currentMinBid&#41;;
      
      HibernateUtil.commitTransaction&#40;&#41;;
      HibernateUtil.closeSession&#40;&#41;;
      ....
      and l don't know whether the new bid will save into the database automatically in the test code above after the session close.
      Or
      may be the answer is there,
      Hibernate Reference 3.1 / 20.5.3. Bags and lists are the most efficient inverse collections , l still not try yet , problem still pending ...

      It seems few people are interested in this problem (or not a problem ?).

      Before l came to this post , l read a lot , including the books you suggested .

      1. the thread "DAO Reference Inside an Entity Domain Object?" many time , and l learn a lot from the post .
      ... http://forum.springframework.org/viewtopic.php?t=5918.
      2. Distributed graphs with optimistic semantics from Hibernate ,
      ... http://forum.hibernate.org/viewtopic.php?t=926083
      ... http://www.hibernate.org/161.html

      moon

      Comment


      • #4
        l came to a strange place after looking for answer these days , and l am still supprising that no body interested in this problem , although it is so common...

        It seem that l cannot have my borrow method to return void , otherwise my DAO have to be inject into my domian object (LibraryUser) in order to save a new borrow , so how about return a borrow instead of a void ? make it like placeBid method ,

        Code:
        	public Borrow borrow&#40;Item item,LibraryUserType libraryUserType&#41; &#123;
        		
        		Borrow newBorrow = new Borrow&#40;&#41;;
        		newBorrow.setItem&#40;item&#41;;
        		newBorrow.setLibraryUser&#40;this&#41;;
        		newBorrow.setLibraryUserType&#40;libraryUserType&#41;;
        
        		Date today = new Date&#40;&#41;;
        		newBorrow.setBorrowDate&#40;today&#41;;
              .....
        		
        		this.getBorrows&#40;&#41;.add&#40;newBorrow&#41;;
        		
        		return newBorrow;
        
        	&#125;
        and make a service class (S) to save borrow instead of saving libraryUser , this will avoid multiple unwanted update to borrows and have transaction to my business logic ,

        Code:
        public class LibraryImpl implements LibraryFacade &#123;
          ........
        	public Borrow borrow&#40;Item item,LibraryUser libraryUser,LibraryUserType libraryUserType&#41;&#123;
        		
        		Borrow newBorrow = libraryUser.borrow&#40;item,libraryUserType&#41;;
        
        		// Instead of save libraryUser , l save a newBorrow.
        		// storeLibraryUser&#40;libraryUser&#41;;
        		// *************************
        		storeBorrow&#40;newBorrow&#41;;
        
        		return newBorrow;
        	&#125;
        
        &#125;
        Controller,
        Code:
        ...
        getLibrary&#40;&#41;.borrow&#40;item,libraryUser,libraryUserType&#41;;
        ...
        getLibrary() will get a service S . ( Do it this way seems like a pseudo OO way. )

        So strange the feeling , because it is natural for me to add a borrow into libraryUser using ,
        Code:
        this.getBorrows&#40;&#41;.add&#40;borrow&#41;;
        l think this is the OO way ,and if we have a transparent persistence tool , the work is DONE here , no more extra persistence code needed , but we are being confined by our persistence tool (relational database) , we have to save the borrow instead of saving a libraryUser , otherwise cause performance hit. When l want to think more "OO" , the persist nature of relational database bend me back "relational" thinking , then my code style will mix two kind of paradigms , one OO , and the other relational ..... am l wrong ? can any body make some noise to let me know that l am not wrestling with myself ....hihihi

        and l found a interesting topic which discuss Anemic Domain Object Model in a chinese forum , "总结一下最近关于domain object以?相关的讨论" ( http://www.hibernate.org.cn/viewtopi...t=%C6%B6%D1%AA ) , l like the way the author , Robbin , analyse the problems , it is very close to my thinking , but unforturenately , the thread is in chinese , l think it will benefit chinese reader.

        moon

        Comment


        • #5
          Originally posted by yfmoan
          l came to a strange place after looking for answer these days , and l am still supprising that no body interested in this problem , although it is so common...
          I think a lot of people are interrested in how to set up a correct domain model and how the DAO/dependencies fit in. Personally I think it is a bad thing the domain objects aren`t created by Spring and don`t contain any dependencies. It reduces them to simple records.

          A few remarks:
          sometimes it is better to introduce some 'management' object. At first it feels natural to do a john.borrow(somebook), but sometimes it is better to introduce something like a Library object and move some of the functionality into that object. You don`t have to design objects just on data, sometimes it is better to design them based on functionality.

          So you do a:
          Libary l;
          l.borrow(john, abook);

          Internally this function finally adds the book to the borrowed list. The advantage? It makes you domain object less complex, and you can groep functionality (that belongs together) into a single object. You can group all library functionality into that object: borrowing books, send email to users that a book has arrived, etc.

          Another remark:
          I don`t think the controller should directly talk to the domain objects to set information. Disadvantages:
          -will be more complicated to add transactions and security.
          -will be more complicated to add certain kinds of logic: I want to be notified when the 1.000.000 book is borrowed.
          -the controllers will ofter contain more logic than a : john.lend(book)

          but a:
          Code:
          if&#40;john.borrowedCount&#40;&#41;>10&#41;&#123;
               ...warning.. you can`t borrow more than 10 books.
          &#125;else&#123;
                john.addBorrow&#40;bool&#41;;
          &#125;
          The controllers contains some logic. This logic could better be moved to a business layer.

          Personally I like to do all my domainobject modifications through a business layer. The controller can talk to the business layer, he doesn`t care how it is solved internal. The business layer contains all the methods that are usefull for the 'outside' world, so you have much more control on what is called. The business layer is also usefull for adding transactions/security. The manager object (Library) I told about, is candidate for a service object in the business layer.

          When l want to think more "OO" , the persist nature of relational database bend me back "relational" thinking , then my code style will mix two kind of paradigms , one OO , and the other relational ..... am l wrong ? can any body make some noise to let me know that l am not wrestling with myself
          This is called the 'The Object-Relational Impedance mismatch' and no.. you are not the only one Join the club.

          Comment


          • #6
            Hi Alarmnummer ,
            Thanks for telling me that l am not wrestling with myself. :wink:

            l think your 'management' object is my service object (S) with different naming.
            sometimes it is better to introduce something like a Library object and move some of the functionality into that object. You don`t have to design objects just on data, sometimes it is better to design them based on functionality.

            So you do a:
            Libary l;
            l.borrow(john, abook);

            Internally this function finally adds the book to the borrowed list. The advantage? It makes you domain object less complex, and you can groep functionality (that belongs together) into a single object. You can group all library functionality into that object: borrowing books, send email to users that a book has arrived, etc.
            You mean l have to move my borrow method out of the libraryUser object ? l think your code above are basically the same with mine in the last post , but with one different , l delegated the borrow method to a service object S , but still maintain the borrow method in libraryUser.

            It seems to me that the description above suggesting that l need to move some of the functions out of the domain object (libraryUser , in my case) , and put into the 'management' object (service or the so called manager object), but this will fall back to the anemic domain object model(ADOM) that l saw in all the spring book's examples, for example , spring in action, pro spring , and even the recent published book - Professional Java Development with the Spring Framework (boxOffice example , see the purchase method's implementation) , all do implementation the same way .

            The best example l can found that not falling into these ADOM category is HIA's caveatemptor example (check it out carefully , pg 305~311) , but unforturenately , the authors say nothing about the performance impact if we want to save borrow using libraryUser , l have point out this problem in my first post .

            I don`t think the controller should directly talk to the domain objects to set information. Disadvantages:
            -will be more complicated to add transactions and security.
            yes , indeed , you are refering my method 2. l am not choosing it as well , it is just a quick way to illustrate the idea ,

            john.lend(book)
            from my latest knowledge , we cannot write code this way in our controller, but
            Code:
            ... 
            getLibrary&#40;&#41;.borrow&#40;item,libraryUser,libraryUserType&#41;; 
            ...
            because l cannot have my borrow method to return void , otherwise my DAO have to be inject into my domian object (LibraryUser) in order to save a new borrow. This is strange for me , l have the feeling : "ai~ ... the relational demon start to bend me away from OO place, because l have a desire to save a borrow , once l have a desire , l must pay back - my borrow function must return a borrow instead of a void"
            The controllers contains some logic. This logic could better be moved to a business layer.
            From my point of view , this kind of business logic suppose to belong to libraryUser , and not the business layer . In my design of this library automation system , if l retrieve a libraryUser Object , l got all the information needed , it is a self-contian domain object , it will be a lot of checks before a libraryUser can borrow an item (book) , let's see some code snipplet ,

            LibraryUser.java (all are the business logic suppose to live in libraryUser , including your borrowedCount() ),
            Code:
            	
            .....
            public boolean isBorrowable&#40;LibraryUserType libraryUserType&#41; &#123;
            		
            		return &#40;isLibraryUserExpiryDateOver&#40;&#41;&&
            				isLibraryUserTypeExpiryDateOver&#40;libraryUserType&#41;&&
            				hasBorrowBlock&#40;libraryUserType&#41;
            				&#41;;
            		
            	&#125;
            	
            	public boolean isLibraryUserExpiryDateOver&#40;&#41; &#123;
            
            		boolean isExpired = false;
            		Date today = new Date&#40;&#41;;
            		if &#40;this.expiryDate.after&#40;today&#41;&#41;
            			isExpired = true;
            
            		return isExpired;
            
            	&#125;
            	
            	public boolean isLibraryUserTypeExpiryDateOver&#40;LibraryUserType libraryUserType&#41; &#123;
            
            		boolean isExpired = false;
            		Date today = new Date&#40;&#41;;
            		if &#40;libraryUserType.getExpiryDate&#40;&#41;.after&#40;today&#41;&#41;
            			isExpired = true;
            
            		return isExpired;
            
            	&#125;
            	
            	public boolean hasBorrowBlock&#40;LibraryUserType libraryUserType&#41; &#123;
            
            		boolean hasBorrowBlock = false;
            
            		if &#40;this.libraryUserBlocks != null&#41; &#123;
            
            			Iterator it = libraryUserBlocks.iterator&#40;&#41;;
            
            			while &#40;it.hasNext&#40;&#41;&#41; &#123;
            
            				LibraryUserBlock libraryUserBlock = &#40;LibraryUserBlock&#41; it.next&#40;&#41;;
            				
            				if &#40;libraryUserBlock.getBorrowBlockStartDate&#40;&#41; != null&#41; &#123;
            					hasBorrowBlock = true;
            					break;
            				&#125;
            
            			&#125;
            
            		&#125;
            
            		return hasBorrowBlock;
            	&#125;
            ....
            // a lot more checking code below , and
            // all the "check code" will go into the borrow method which l have neglected in my first post 
            // ***********************************************************************************
            you can see from the code above that l have all the self-contain business logic inside the libraryUser , once you got it , you got all the info you want in the libraryUser , this is the power of domain object model.

            Although l came to this place , l still not satisfied , l have to write pseudo OO code just because l don't want an AMOD design ? This is just a beginning to write pseudo code , what will happen if l go furture ? Oh~ cannot imagine ....

            l am a newbie in the field , there must be somebody gone a long way than me , can any body give me some advise , don't you have the problem as mine ? This is just a common problem if you want to write a rich DOM code ! ... huh ~~

            moon

            Comment


            • #7
              Originally posted by yfmoan
              but this will fall back to the anemic domain object model(ADOM) that l saw in all the spring book's examples, for example , spring in action, pro spring , and even the recent published book - Professional Java Development with the Spring Framework (boxOffice example , see the purchase method's implementation) , all do implementation the same way .
              It doesn`t have to mean you have to move all logic from the domain models (this is what you do with an anemic domain model). But some logic can be moved, and I think it is always better to introduce a Service layer your system can use (even if the service layers passed the call directly to the domain objects)


              relational demon start to bend me away from OO place, because l have a desire to save a borrow , once l have a desire , l must pay back - my borrow function must return a borrow instead of a void"
              The problem is not the relational model, but the lack of dependencies in your domain object. If you had the dao in your domain objects, there would be no problem and I think this is the main cause why it is so easy to create an anemic domain model with Spring.

              Normally you would do 'bad' things like Singletons to get to the DAO`s for example, but because it is bad, and there is not useable other solution, we don`t use DAO`s (or other dependencies) at all in domain objects!

              From my point of view , this kind of business logic suppose to belong to libraryUser , and not the business layer .
              There are 2 different levels of logic:
              1) application logic
              2) domain logic.

              If you want to send an email if the 1.000.000 book is borrowed, this normally would be a piece of application logic and you don`t add this in your domain objects.

              In my design of this library automation system , if l retrieve a libraryUser Object , l got all the information needed , it is a self-contian domain object , it will be a lot of checks before a libraryUser can borrow an item (book) , let's see some code snipplet ,
              You are right, this is domain logic.

              Although l came to this place , l still not satisfied , l have to write pseudo OO code just because l don't want an AMOD design ? This is just a beginning to write pseudo code , what will happen if l go furture ? Oh~ cannot imagine ....
              Join the club

              l am a newbie in the field , there must be somebody gone a long way than me , can any body give me some advise , don't you have the problem as mine ? This is just a common problem if you want to write a rich DOM code ! ... huh ~~
              There are a lot of people on the Spring forum struggeling with these problems. I`m one of them also. And if you want I can give you a complete new area of problems: webapplications and binding to domain objects. If you allow webapplications to bind directly to domain objects (you do this when you fill in a form to save time), your service layer needs some 'save(entity)' method and this is going to wreck your service layer. All your nice methods like: fire(Employee) or lend(Book) can be bypassed with the save method (because you can save every objects and the state doesn`t matter).

              Comment


              • #8
                Alarmnummer ,

                Sorry because l have to ask a sharp question.

                Since l am a newbie , l did not write a single application , not until this library system is done . l can make an excuses myself , but a lot of professional programmers out there are using their skill to make a living , they must have some completed projects , even this problem (may be not ) bother them. How they (or you) write rich DOM code , any work around ?

                Do you agree that my borrow method be placed in the libraryUser ? If so , then how you solve the multi update problem ?

                - avoiding the problem by going back to anemic model ?
                - write pseudo code as mine (this is anemic way too , although l hide the code in service layer)?
                - any other options ?

                Normally you would do 'bad' things like Singletons to get to the DAO`s for example,
                don't understand this part , can you explain further ?

                if you want I can give you a complete new area of problems: webapplications and binding to domain objects
                can you explian this too , or pointing me to the resources , l would like to learn more ,

                moon

                Comment


                • #9
                  Originally posted by yfmoan
                  Alarmnummer ,

                  Sorry because l have to ask a sharp question.

                  Since l am a newbie , l did not write a single application , not until this library system is done . l can make an excuses myself , but a lot of professional programmers out there are using their skill to make a living , they must have some completed projects , even this problem (may be not ) bother them. How they (or you) write rich DOM code , any work around ?
                  -If you are a 'professional' you have learned to give up hope and use what you have got. You have learned that the struggeling is normal and finally you accept it as the way you do your work and start to defend it if others complain because it sucks. In some cases you even make up 'design patterns' with complicated names to deal with the problems of the bad solution.
                  -Or you are to stupid to realise it sucks in the first place and do it because this is the way everybody does it.
                  -Or you find it great because: if it is so difficult to use, it must be good!

                  I`m a littlebit sarcastic, but I hope you get the point.

                  btw: this could be applied to EJB-programmers also

                  Do you agree that my borrow method be placed in the libraryUser ? If so , then how you solve the multi update problem ?
                  It can be placed on the library user. But it should never be could from other layers than your business layer. in some cases the business layer only passes the calls to the domain objects. The service/business layer is the gateway between your domain objects/dao`s and the rest of the world.

                  - avoiding the problem by going back to anemic model ?
                  - write pseudo code as mine (this is anemic way too , although l hide the code in service layer)?
                  With Spring it is quite normal to create an anemic domain model is my conclusion so far. I add some logic to domain objects, but not much because I get into problems as soon as the domain object needs some dependency.


                  Normally you would do 'bad' things like Singletons to get to the DAO`s for example,
                  don't understand this part , can you explain further ?
                  In the good old days, you created a singleton if you needed a reference to an object. You need a PersonDao instance when you are in a person object? Just call: PersonDao.getInstance(). So a Singleton is a way to get an object reference if you need it. With Spring, singletons aren`t needed anymore (you can inject all the references into your objects). The problem is dat domain objects aren`t created by Spring, but by the OR mapper so they don`t contain any dependency.

                  And because you dropped all the singletons, the domain objects can`t get a dependency at all.

                  if you want I can give you a complete new area of problems: webapplications and binding to domain objects
                  can you explian this too , or pointing me to the resources , l would like to learn more ,
                  moon
                  Example:

                  Code:
                  class EmployeeService&#123;
                  
                       void raise&#40;Employee e, int amount&#41;&#123;
                            e.setSalary&#40;e.getSalary&#40;&#41;+amount&#41;;
                            employeeDao.save&#40;e&#41;;
                            if&#40;e.getSalary&#40;&#41;>ceo.getSalary&#40;&#41;&&!e.equals&#40;ceo&#41;&#41;&#123;
                                 raise&#40;ceo,e.getAmount&#40;&#41;+10000&#41;;               
                            &#125;
                       &#125;
                  
                      void save&#40;Employee e&#41;&#123;
                              employeeDao.save&#40;e&#41;;
                       &#125;
                  &#125;
                  If I want to give somebody a raise, I can call:

                  employeeService.raise(john,100);

                  and if he earns more than the ceo, the ceo his salary is raised to.

                  Ok.. but what if I do the following?
                  empoyee.setSalary(100000000);
                  employeeService.save(employee);

                  In this case the CEO doesn`t get his raise.


                  And the save method is needed if you bind information to the domain objects and need to save it. So a save method can bypass all logic.

                  Comment


                  • #10
                    Originally posted by yfmoan
                    because l cannot have my borrow method to return void , otherwise my DAO have to be inject into my domian object (LibraryUser) in order to save a new borrow.
                    Moon,

                    I will be the first in line to criticize Spring for not making this quite as seamless as it should be, but it is far from impossible, and in fact I do it everywhere in my code.

                    How is LibraryUser being instantiated?

                    If it is being instantiated by a factory-type Spring service, then you are good to go. The factory simply calls libraryUser.setLibraryUserDao ( _libraryUserDao ), or you can add LibraryUserDao to the object's constructor as a valid dependency. If you have a lot of dependencies in your domain object, you might make your factory ApplicationContextAware, and have the factory autowire your domain object after construction.

                    If it is being instantiated by Hibernate, then you need to use DependencyInjectionInterceptorFactoryBean to populate the dependencies in your domain object.

                    If it is being instantiated by a different third-party framework... well, then you must retreat into Alarmnummer's pit of despair until AspectJ 5 integration is released. :wink:

                    Comment


                    • #11
                      found a second work around method , this is more OO - avoid using detached object .

                      Since the problem is caused by the hibernate detached object which cannot recognise the dirty properties changed if select-before-update attribute is not set to "true" , we just do our best to avoid it.

                      my problem is caused by the "reuse" of libraryUser which are return from formBackingObject() to onSubmit() in BorrowForm.java ,

                      Code:
                      public class BorrowForm extends AbstractLibraryFormController &#123;
                      
                      	public BorrowForm&#40;&#41; &#123;
                      		setCommandClass&#40;LibraryUser.class&#41;;
                      		setCommandName&#40;"libraryUser"&#41;;
                      		setSessionForm&#40;true&#41;;
                      	&#125;
                      	
                      
                      	protected Object formBackingObject&#40;HttpServletRequest request&#41;
                      			throws ServletException &#123;
                      
                      		String cardKey = request.getParameter&#40;"cardKey"&#41;;
                         ...
                      		LibraryUser libraryUser = getLibrary&#40;&#41;.findLibraryUser&#40;cardKey&#41;;
                         ...
                      		return libraryUser;
                      	&#125;
                         ...
                      	public ModelAndView onSubmit&#40;HttpServletRequest request,
                      			HttpServletResponse response, Object command, BindException errors&#41;
                      			throws Exception &#123;
                      
                      		String barcode = request.getParameter&#40;"barcode"&#41;;
                      		long lutId = RequestUtils.getLongParameter&#40;request,"libraryUserTypeId", 0&#41;;
                      
                      		// the below libraryUser is a detached object ! 
                      		// l cannot reuse it ! it cause difficulty l have mention in my first post.
                      		// **********************************************************************
                      		LibraryUser libraryUser = &#40;LibraryUser&#41; command;
                      
                      		Long libraryUserId = libraryUser.getId&#40;&#41;;
                      		Long libraryUserTypeId = new Long&#40;lutId&#41;;
                      
                      		// l get another libraryuser inside the method lend&#40;..&#41; , see the code below
                      		// *********************************************************************
                      		getLibrary&#40;&#41;.lend&#40;barcode,libraryUserId,libraryUserTypeId&#41;;
                      
                      		return new ModelAndView&#40;getSuccessView&#40;&#41;+ "?" + "cardKey="
                      				+ libraryUser.getCardKey&#40;&#41;&#41;;
                      
                      	&#125;
                      my service class LibraryImpl.java ,
                      Code:
                      public class LibraryImpl implements LibraryFacade &#123;
                      ...
                      	public void lend&#40;String barcode,Long libraryUserId,Long libraryUserTypeId&#41;&#123;
                      		
                      		LibraryUser libraryUser = loadLibraryUser&#40;libraryUserId.longValue&#40;&#41;&#41;;
                      		LibraryUserType libraryUserType = null;
                      		
                      		Set libraryUserTypes = libraryUser.getLibraryUserTypes&#40;&#41;;
                      		Iterator it = libraryUserTypes.iterator&#40;&#41;;
                      		
                      		while &#40;it.hasNext&#40;&#41;&#41; &#123;
                      
                      			LibraryUserType lut = &#40;LibraryUserType&#41; it.next&#40;&#41;;
                      
                      			if &#40;lut.getId&#40;&#41;.equals&#40;libraryUserTypeId&#41;&#41; &#123;
                      				libraryUserType = lut;
                      				break;
                      			&#125;
                      		&#125;
                      
                      		Item item = loadItemByBarcode&#40;barcode&#41;;
                      		
                      		libraryUser.lend&#40;item,libraryUserType&#41;;
                      
                      		// l can use storeLibraryUser&#40;..&#41; now , it won't cause multi-unwanted update 
                      		// ***********************************************************************
                      		storeLibraryUser&#40;libraryUser&#41;;
                      		
                      	&#125;
                      &#125;
                      LibraryUser.java's lend method , notice that it is return a void instead of a borrow ,
                      Code:
                      	public void lend&#40;Item item,LibraryUserType libraryUserType&#41; &#123;
                      		
                      		Borrow newBorrow = new Borrow&#40;&#41;;
                      		newBorrow.setItem&#40;item&#41;;
                      		newBorrow.setLibraryUser&#40;this&#41;;
                      		newBorrow.setLibraryUserType&#40;libraryUserType&#41;;
                      
                      		Date today = new Date&#40;&#41;;
                      		newBorrow.setBorrowDate&#40;today&#41;;
                      		
                      		long oneDay = 86400000;
                      		// check the convertion OK or not -> int to long
                      		long duration = libraryUserType.getDuration&#40;&#41;.longValue&#40;&#41;;
                      		long itemDuration = item.getItemDuration&#40;&#41;.getItemDurationValue&#40;&#41;.longValue&#40;&#41;;
                      		Date dueDate =  new Date&#40;today.getTime&#40;&#41;+&#40;duration*oneDay&#41;+&#40;itemDuration*oneDay&#41;&#41;;
                      		newBorrow.setDueDate&#40;dueDate&#41;;
                      		
                      		this.getBorrows&#40;&#41;.add&#40;newBorrow&#41;;
                      		
                      	&#125;
                      it insert only one borrow , hibernate knew who have been modified.
                      Code:
                      ...
                      Hibernate&#58; insert into BORROWS &#40;VERSION, BORROW_DATE, DUE_DATE, RETURN_DATE, REPORTLOST_DATE, RENEWED_NO, LIBRARY_USER_TYPE_ID, ITEM_ID, USER_ID&#41; values &#40;?, ?, ?, ?, ?, ?, ?, ?, ?&#41;
                      ...
                      Remark :

                      1. This is fast code , just to illustrate idea.
                      2. l don't know what will happen if l keep avoiding detached object , l think this is not better than the psuedo code.

                      Alarmnummer , thanks for the explaination , but l still don't understand second answer
                      And the save method is needed if you bind information to the domain objects and need to save it. So a save method can bypass all logic.
                      it seems that you are exposing your service object for someone to take control , ?? .

                      -If you are a 'professional' you have learned to give up hope and use what you have got. You have learned that the struggeling is normal and finally you accept it as the way you do your work and start to defend it if others complain because it sucks. In some cases you even make up 'design patterns' with complicated names to deal with the problems of the bad solution.
                      -Or you are to stupid to realise it sucks in the first place and do it because this is the way everybody does it.
                      -Or you find it great because: if it is so difficult to use, it must be good!

                      I`m a littlebit sarcastic, but I hope you get the point.
                      Yes , l understood .

                      moon

                      Comment


                      • #12
                        Hi cepage ,

                        How is LibraryUser being instantiated?
                        my libraryUser is instantiated by Hibernate, l have thought about to use DependencyInjectionInterceptorFactoryBean to inject the dependencies , but this will make my libraryUser depend on DAO or service object , although oliverhutchison wrote in another post "DAO Reference Inside an Entity Domain Object?" that :
                        As long as you services layer is defined with an interface there's nothing to fear.
                        l am not going through this way now , basic instinct told me not to , hihi , may be l will try it later when l hit the stone , . The way l am chosing fall in the second category model in the chinese forum l described above (It's supposed to be a rich DOM), l will report back if l hit stone.

                        Remark:

                        The chinese author , robbin , summarize three model (l describe roughly):
                        1. ADOM
                        2. Rich DOM (domain not depend on DAO)
                        3. Rich DOM (domain depend on DAO)

                        If you don't know chinese , you still can see from the codes to guess what he means , , l cannot translate the article without the permission of the author (it is not easy to translate 2 , because l don't understand a lot , :oops: ).


                        It seem that you are writing application with DAO inject into domain object (This fall into the third category model), did you hit any stone ?

                        moon

                        Comment

                        Working...
                        X