IBM Support

PI47144: MERGING AN UNMANAGED ENTITY MULTIPLE (3) TIMES LEADS TO AN EXCEPTION.

Subscribe

You can track all active APARs for this component.

 

APAR status

  • Closed as program error.

Error description

  • There is a scenario, albeit a very odd one, where by doing
    multiple 'merge' calls on the same unmanaged entity causes an
    exception. It is an 'odd' case because of the fact that an
    'unmanaged' entity is being merged multiple times. The proper
    way to handle the scenario is to merge the managed instance.
    Here are some code snippets that can be used to explain the
    issue:
    
    @Entity
    @IdClass( LineItemPK.class )
    public class LineItem {
    @Id
    @Column( name = "ORDER_ID", nullable = false )
    private Long orderId;
    
    @Id
    @Column( name = "ITEM_ID", nullable = false )
    private Long itemId;
    ......
    
    @Embeddable
    public class LineItemPK implements Serializable {
    @Column( name = "ORDER_ID", nullable = false )
    private Long orderId;
    
    @Column( name = "ITEM_ID", nullable = false )
    private Long itemId;
    ......
    
    @Entity
    @Table( name = "ORDER_TABLE" )
    public class Order {
    @Id
    @Column( name = "ID", nullable = false )
    private Long id;
    
    @OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL )
    @JoinColumn( name = "ORDER_ID", referencedColumnName = "ID" )
    private List<LineItem> items;
    ......
    
    With these classes, take this test:
    
    em.getTransaction().begin();
    Order order = new Order( 1l );
    
    LineItem item = new LineItem( "my product", 44, 4.99f );
    order.addItem(item);
    
    //NOTE: Notice that throughout the rest of the test the
    unmanaged order is //merged. Throughout the rest of the test we
    should do a
    //'order = em.merge(order)', or something to that effect (i.e.
    use the //'managed' order). However, technically speaking
    merging the unmanaged //order is not wrong, albeit odd and
    potentially error prone.
    em.merge(order);
    em.getTransaction().commit();
    
    em.getTransaction().begin();
    LineItem additional = new LineItem( "My second product", 1,
    999.95f );
    order.addItem(additional);
    order.setOrderEntry( new Date( System.currentTimeMillis() ) );
    em.merge(order);
    //NOTE: do a flush here and all works fine:
    //em.flush();
    em.merge(order);
    em.getTransaction().commit();
    
    As you can see, the unmanaged order is merged. As my comments
    above suggest this is odd, but technically not wrong. What
    makes this case interesting is that if we change LineItem to
    use a single PK rather than a compound PK, all works fine! The
    issue can also be resolved by performing a strategic 'flush' as
    commented above. Furthermore, the exception the above test
    yields is:
    
    Caused by: <openjpa-2.1.2-SNAPSHOT-r422266:1686894M fatal
    general error>
    org.apache.openjpa.persistence.PersistenceException: Column
    'ORDER_ID' cannot accept a NULL value.
    {prepstmnt 27085446 UPDATE ITEM_TABLE SET ORDER_ID = ? WHERE
    ORDER_ID = ? [params=(null) null, (long) 1]}
    
    [code=20000, state=23502]
    
    This is rather meaningless and is caused because OpenJPA
    executes this SQL when doing the third merge:
    
    openjpa.jdbc.SQL - <t 3968441, conn 30267242> executing
    prepstmnt 27085446 UPDATE ITEM_TABLE SET ORDER_ID = ? WHERE
    ORDER_ID = ? [params=(null) null, (long) 1]
    
    Given the odd message, and the fact that things work for the
    same exact scenario when a single PK is used, and the fact that
    it can be resolved with a 'flush', it makes sense to fix this
    issue.
    
    Finally, I can't stress enough that the proper way to perform
    the above test is to use the MANAGED version of the 'order'. In
    other words, replace all 'em.merge(order)' with 'order =
    em.merge(order)'. The above scenario creates far more SQL
    statements because of merging an unmanaged entity than if you
    merged a managed entity.
    

Local fix

  • The proper way to perform the above test is to use the MANAGED
    version of the 'order'. In other words, replace all
    'em.merge(order)' with 'order = em.merge(order)'. The above
    scenario creates far more SQL statements because of merging an
    unmanaged entity than if you merged a managed entity.
    

Problem summary

  • ****************************************************************
    * USERS AFFECTED:  All users of IBM WebSphere Application      *
    *                  Server V8.0.0, V8.5.0, and V8.5.5 who       *
    *                  perform a 'merge' on an unmanaged entity    *
    *                  multiple times.                             *
    ****************************************************************
    * PROBLEM DESCRIPTION: Merging an unmanaged entity multiple    *
    *                      times can lead to nulls values being    *
    *                      persisted, which can cause an           *
    *                      exception.                              *
    ****************************************************************
    * RECOMMENDATION:                                              *
    ****************************************************************
    Take the following code snippets:
    @Entity
    @IdClass( LineItemPK.class )
    public class LineItem {
    @Id
    @Column( name = "ORDER_ID", nullable = false )
    private Long orderId;
    @Id
    @Column( name = "ITEM_ID", nullable = false )
    private Long itemId;
    ......
    @Embeddable
    public class LineItemPK implements Serializable {
    @Column( name = "ORDER_ID", nullable = false )
    private Long orderId;
    @Column( name = "ITEM_ID", nullable = false )
    private Long itemId;
    ......
    @Entity
    @Table( name = "ORDER_TABLE" )
    public class Order {
    @Id
    @Column( name = "ID", nullable = false )
    private Long id;
    @OneToMany( fetch = FetchType.EAGER, cascade = CascadeType.ALL
    )
    @JoinColumn( name = "ORDER_ID", referencedColumnName = "ID" )
    private List<LineItem> items;
    ......
    With these classes, take this test:
    em.getTransaction().begin();
    Order order = new Order( 1l );
    LineItem item = new LineItem( "my product", 44, 4.99f );
    order.addItem(item);
    //NOTE: Notice that throughout the rest of the test the
    //unmanaged order is merged. Throughout the rest of the
    //test we should do a 'order = em.merge(order)', or
    //something to that effect (i.e. use the 'managed' order).
    //However, technically speaking merging the unmanaged
    //order is not wrong, albeit odd and potentially error prone.
    em.merge(order);
    em.getTransaction().commit();
    em.getTransaction().begin();
    LineItem additional = new LineItem( "tst1", 1, 9.95f );
    order.addItem(additional);
    order.setOrderEntry( new Date( System.currentTimeMillis() ) );
    em.merge(order);
    //NOTE: do a flush here and all works fine:
    //em.flush();
    em.merge(order);
    em.getTransaction().commit();
    As can be seen with this code and test, the unmanaged order
    is merged multiple times.  As the java comments above suggest
    this is odd, but technically not wrong.  The java comments
    above explain how the issue can be resolved by merging the
    managed order.  The issue can also be resolve by performing a
    strategic 'flush' as commented above.
    The exception the above test yields is:
    Caused by: <openjpa-2.1.2-SNAPSHOT-r422266:1686894M fatal
    general error>
    org.apache.openjpa.persistence.PersistenceException: Column
    'ORDER_ID' cannot accept a NULL value.
    {prepstmnt 27085446 UPDATE ITEM_TABLE SET ORDER_ID = ? WHERE
    ORDER_ID = ? [params=(null) null, (long) 1]}
    This exception occurs because OpenJPA executes the following
    SQL when doing the third merge:
    openjpa.jdbc.SQL - <t 3968441, conn 30267242> executing
    prepstmnt 27085446 UPDATE ITEM_TABLE SET ORDER_ID = ? WHERE
    ORDER_ID = ? [params=(null) null, (long) 1]
    In this case, OpenJPA is attempting to persist 'null' to a
    column which is defined as not allowing non-null values.
    It should be noted that the proper way to perform the above
    test is to use the MANAGED version of the 'order'.  In other
    words, replace all 'em.merge(order)' with
    'order = em.merge(order)'.  Furthermore, the above scenario
    creates far more SQL statements than is needed because it
    merging an unmanaged entity.  Merging the managed entity in
    this test would produce fewer SQL statements than merging the
    unmanaged entity.
    

Problem conclusion

  • With this fix, code has been added to OpenJPA to ensure that
    OpenJPA doesn't attempt to persist a null value to a non-null
    column when an entity is merged multiple times.
    
    The fix for this APAR is currently targeted for
    inclusion in Service Levels (Fix Packs) 8.0.0.12 and
    8.5.5.9 of WebSphere Application Server versions 8.0.0 and
    8.5.5.
    
    Please refer to the recommended updates page for delivery
    information:
    http://www.ibm.com/support/docview.wss?rs=180&uid=swg27004980
    

Temporary fix

Comments

APAR Information

  • APAR number

    PI47144

  • Reported component name

    WEBS APP SERV N

  • Reported component ID

    5724H8800

  • Reported release

    800

  • Status

    CLOSED PER

  • PE

    NoPE

  • HIPER

    NoHIPER

  • Special Attention

    NoSpecatt / Xsystem

  • Submitted date

    2015-08-19

  • Closed date

    2015-10-19

  • Last modified date

    2015-10-19

  • APAR is sysrouted FROM one or more of the following:

  • APAR is sysrouted TO one or more of the following:

Fix information

  • Fixed component name

    WEBS APP SERV N

  • Fixed component ID

    5724H8800

Applicable component levels

  • R800 PSY

       UP

  • R850 PSY

       UP

[{"Business Unit":{"code":"BU059","label":"IBM Software w\/o TPS"},"Product":{"code":"SSEQTP","label":"WebSphere Application Server"},"Platform":[{"code":"PF025","label":"Platform Independent"}],"Version":"8.0","Line of Business":{"code":"LOB45","label":"Automation"}}]

Document Information

Modified date:
27 April 2022