OpenJPA
  1. OpenJPA
  2. OPENJPA-1569

@Strategy triggers an InvalidStateException for fields which are declared as Java interfaces

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 1.2.2, 2.0.0-beta2
    • Fix Version/s: 1.2.3, 2.0.0
    • Component/s: kernel
    • Labels:
      None
    • Environment:
      MacOS X, Spring 3.0, Maven
    • Patch Info:
      Patch Available

      Description

      The @Strategy annotation works fine for classes but does not work if the field is a Java interface. Consider:

      @Persistent
      @Strategy("SimpleHandler")
      @Column(name="address_packed")
      private BaseAddress address;
      public BaseAddress getAddress()

      { return address; }

      public void setAddress(BaseAddress address)

      { this.address = address; }

      This works perfectly if BaseAddress is defined as a class but results in an InvalidStateException if it is an interface:

      <openjpa-2.0.0-beta2-rexported fatal user error> org.apache.openjpa.persistence.InvalidStateException: Encountered unmanaged object in persistent field "com.example.SimpleEntity.address" during flush. However, this field does not allow cascade persist. Set the cascade attribute for this field to CascadeType.PERSIST or CascadeType.ALL (JPA annotations) or "persist" or "all" (JPA orm.xml), or enable cascade-persist globally, or manually persist the related field value prior to flushing. You cannot flush unmanaged objects or graphs that have persistent associations to unmanaged objects.
      FailedObject: com.example.USAddress@4e0a39de
      at org.apache.openjpa.kernel.SingleFieldManager.preFlushPC(SingleFieldManager.java:767)
      at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:614)
      at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:575)
      at org.apache.openjpa.kernel.SingleFieldManager.preFlush(SingleFieldManager.java:491)
      at org.apache.openjpa.kernel.StateManagerImpl.preFlush(StateManagerImpl.java:2956)
      at org.apache.openjpa.kernel.PNewState.beforeFlush(PNewState.java:40)
      at org.apache.openjpa.kernel.StateManagerImpl.beforeFlush(StateManagerImpl.java:1048)
      at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:2051)
      at org.apache.openjpa.kernel.BrokerImpl.flushSafe(BrokerImpl.java:2011)
      at org.apache.openjpa.kernel.BrokerImpl.flush(BrokerImpl.java:1782)
      at org.apache.openjpa.kernel.DelegatingBroker.flush(DelegatingBroker.java:1015)
      at org.apache.openjpa.persistence.EntityManagerImpl.flush(EntityManagerImpl.java:637)
      ... etc ...

      Here com.example.USAddress is a class which implements the BaseAddress interface.

      1. OPENJPA-1569-1.2.x.patch
        0.6 kB
        Heath Thomann
      2. patch.txt
        0.6 kB
        Jerry Carter

        Activity

        Jerry Carter created issue -
        Hide
        Jerry Carter added a comment - - edited

        Chased this down into the code for a bit. In SingleFieldManager.preFlush(FieldMetaData, int, int, int, boolean, boolean, OpCallbacks), interfaces have a type of JavaTypes.PC_UNTYPED whereas classes are JavaTypes.OBJECT. The decision logic for setting this value appears to come from org.apache.openjpa.meta.JavaTypes::getTypeCode(Class<?> type):

        if (type.isInterface())

        { if (type == Serializable.class) return OBJECT; return PC_UNTYPED; }

        Here the interface (BaseAddress) enters the outer 'if' and returns PC_UNTYPED whereas the actual class (USAddress) skips this section.

        Show
        Jerry Carter added a comment - - edited Chased this down into the code for a bit. In SingleFieldManager.preFlush(FieldMetaData, int, int, int, boolean, boolean, OpCallbacks), interfaces have a type of JavaTypes.PC_UNTYPED whereas classes are JavaTypes.OBJECT. The decision logic for setting this value appears to come from org.apache.openjpa.meta.JavaTypes::getTypeCode(Class<?> type): if (type.isInterface()) { if (type == Serializable.class) return OBJECT; return PC_UNTYPED; } Here the interface (BaseAddress) enters the outer 'if' and returns PC_UNTYPED whereas the actual class (USAddress) skips this section.
        Hide
        Fay Wang added a comment -

        Jerry, you are right. the field of address, which is of type BaseAddress is treated as PC_UNTYPED, which causes the exception to be thrown in the later stage.

        @Persistent
        @Strategy("SimpleHandler")
        @Column(name="address_packed")
        private BaseAddress address;
        public BaseAddress getAddress()

        { return address; }
        public void setAddress(BaseAddress address) { this.address = address; }

        The workaround is to do the following:

        @Persistent
        @Strategy("SimpleHandler")
        @Column(name="address_packed")
        private java.io.Serializable address;
        public java.io.Serializable getAddress() { return address; }

        public void setAddress(java.io.Serializable address)

        { this.address = address; }

        where your BaseAddress:

        public interface BaseAddress extends java.io.Serializable

        { public String getStreet(); public void setStreet(String street); }
        Show
        Fay Wang added a comment - Jerry, you are right. the field of address, which is of type BaseAddress is treated as PC_UNTYPED, which causes the exception to be thrown in the later stage. @Persistent @Strategy("SimpleHandler") @Column(name="address_packed") private BaseAddress address; public BaseAddress getAddress() { return address; } public void setAddress(BaseAddress address) { this.address = address; } The workaround is to do the following: @Persistent @Strategy("SimpleHandler") @Column(name="address_packed") private java.io.Serializable address; public java.io.Serializable getAddress() { return address; } public void setAddress(java.io.Serializable address) { this.address = address; } where your BaseAddress: public interface BaseAddress extends java.io.Serializable { public String getStreet(); public void setStreet(String street); }
        Hide
        Jerry Carter added a comment -

        (Set priority to minor as there are a number of workarounds.)

        While some argument might be made for supporting arbitrary types as recipients for @Strategy decoration, the spirit of JPA limits one to "user-defined types that implement the Serializable interface" (JPA 2.0, section 2.2). This is probably why the distinction is made in JavaTypes, but the implementation might be wrong.

        Rather than 'type == Serializable.class', mightn't this be 'Serializable.class.isAssignableFrom(type)'?

        Show
        Jerry Carter added a comment - (Set priority to minor as there are a number of workarounds.) While some argument might be made for supporting arbitrary types as recipients for @Strategy decoration, the spirit of JPA limits one to "user-defined types that implement the Serializable interface" (JPA 2.0, section 2.2). This is probably why the distinction is made in JavaTypes, but the implementation might be wrong. Rather than 'type == Serializable.class', mightn't this be 'Serializable.class.isAssignableFrom(type)'?
        Jerry Carter made changes -
        Field Original Value New Value
        Priority Major [ 3 ] Minor [ 4 ]
        Hide
        Jerry Carter added a comment -

        I've attached a patch for the proposed solution - switching to 'Serializable.class.isAssignableFrom(type)'. This addresses the issue and does not break any of the regression tests included with OpenJPA 2.0.0-beta3 or my own JPA tests. I believe this change should be made for 2.0.0.

        Show
        Jerry Carter added a comment - I've attached a patch for the proposed solution - switching to 'Serializable.class.isAssignableFrom(type)'. This addresses the issue and does not break any of the regression tests included with OpenJPA 2.0.0-beta3 or my own JPA tests. I believe this change should be made for 2.0.0.
        Jerry Carter made changes -
        Attachment patch.txt [ 12439744 ]
        Donald Woods made changes -
        Assignee Donald Woods [ drwoods ]
        Donald Woods made changes -
        Patch Info [Patch Available]
        Component/s kernel [ 12311302 ]
        Donald Woods made changes -
        Status Open [ 1 ] Resolved [ 5 ]
        Fix Version/s 2.0.0 [ 12314019 ]
        Resolution Fixed [ 1 ]
        Donald Woods made changes -
        Status Resolved [ 5 ] Closed [ 6 ]
        Hide
        Heath Thomann added a comment -

        Attaching a patch, named OPENJPA-1569-1.2.x.patch, for 1.2.x to fix the issue there.

        Thanks,

        Heath

        Show
        Heath Thomann added a comment - Attaching a patch, named OPENJPA-1569 -1.2.x.patch, for 1.2.x to fix the issue there. Thanks, Heath
        Heath Thomann made changes -
        Attachment OPENJPA-1569-1.2.x.patch [ 12466766 ]
        Rick Curtis made changes -
        Fix Version/s 1.2.2 [ 12313681 ]
        Affects Version/s 1.2.2 [ 12313681 ]
        Jeremy Bauer made changes -
        Resolution Fixed [ 1 ]
        Status Closed [ 6 ] Reopened [ 4 ]
        Jeremy Bauer made changes -
        Fix Version/s 1.2.3 [ 12314517 ]
        Fix Version/s 1.2.2 [ 12313681 ]
        Hide
        Jeremy Bauer added a comment -

        Change for 1.2.x was committed under revision r1053401. The commit was mis-tagged to OPENJPA-1596.

        Show
        Jeremy Bauer added a comment - Change for 1.2.x was committed under revision r1053401. The commit was mis-tagged to OPENJPA-1596 .
        Jeremy Bauer made changes -
        Status Reopened [ 4 ] Closed [ 6 ]
        Resolution Fixed [ 1 ]

          People

          • Assignee:
            Donald Woods
            Reporter:
            Jerry Carter
          • Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development