Grails
  1. Grails
  2. GRAILS-9026

Domain unit test with bidirectional one to many using List causes duplicates

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Duplicate
    • Affects Version/s: 2.0.3
    • Fix Version/s: None
    • Component/s: Persistence, Testing
    • Labels:
    • Environment:
      JVM: 1.6.0_29 Vendor: Apple Inc. OS: Mac OS X
    • Testcase included:
      yes

      Description

      Given a domain class Order which has a bidirectional association to a list of Line:

      package test
      
      class Order {
      	List<Line> lines = []
      	static hasMany = [lines: Line]
      }
      
      package test
      
      class Line {
      	static belongsTo = [order:Order]
      }
      

      The following unit test will fail:

      package test
      
      import grails.test.mixin.Mock
      import grails.test.mixin.TestFor
      
      @TestFor(Order)
      @Mock(Line)
      class OrderTests {
      	void testSave() {
      		Order order = new Order()
      		order.save()
      		assert order.id != null
      		
      		order.addToLines new Line()
      		order.save()
      		assert order.lines.size() == 1
      	}
      }
      

      because order.lines.size() is 2 and the line is in the list twice.

      It looks like the problem may be in org.grails.datastore.mapping.engine.NativeEntryEntityPersister:

      the duplicate element is being added to the list by the following code lines (886-899):

      if (inverseEntity != null) {
      	EntityAccess inverseAccess = createEntityAccess(association.getAssociatedEntity(), inverseEntity);
      	if (inverse instanceof OneToMany) {
      		Collection existingValues = (Collection) inverseAccess.getProperty(inverse.getName());
      		if (existingValues == null) {
      			existingValues = MappingUtils.createConcreteCollection(inverse.getType());
      			inverseAccess.setProperty(inverse.getName(), existingValues);
      		}
      		existingValues.add(entityAccess.getEntity());
      	}
      	else if (inverse instanceof ToOne) {
      		inverseAccess.setProperty(inverse.getName(), entityAccess.getEntity());
      	}
      }
      

      It seems like the following:

      existingValues.add(entityAccess.getEntity())
      

      line may need to be inside the if block:

      if (existingValues == null) {
      ...
      }
      

      Running the same test in an integration test works as expected.

        Activity

        Hide
        Egor Ermakov added a comment -

        I can confirm this bug. We have same issue.

        Show
        Egor Ermakov added a comment - I can confirm this bug. We have same issue.

          People

          • Assignee:
            Graeme Rocher
            Reporter:
            Charles Scherer
          • Votes:
            1 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development