Search code examples
javamergepersistenceeclipselinkentitymanager

EntityManager.persist() works, but EntityManager.merge() does not


We are using EclipseLink 2.5.2 and WildFly 9.0.0.

Here is a method that gets called a certain number of times every hour (specified by an EJB timer). For my testing purposes, I've changed it to fire off every minute, and in my specific configuration this method will get called 10 times per minute since there are 10 updates to be made:

@Stateless
public class NetCentricDataService {

    @PersistenceContext(unitName = "NetCentricPersistenceUnit")
    private EntityManager em;

    public void insertUpdate(A a, B b, boolean insert) {
        if (insert) {
            em.persist(a);
            em.flush();
            if (b != null) {
                em.persist(b);
                em.flush();
            }
        } else {
            em.merge(a);
            if (b != null) {
                em.merge(b);
            }
            em.flush();
        }
    }
}

When I clear the tables associated with the two entity classes below, the if(insert) block executes, the persist calls work fine, and the DB is updated; however, if there are existing entries in the DB, the else block gets executed, the merge calls run with no Exceptions, but the DB is not updated. I am checking this in SQL Developer after the method call is complete.

EDIT: For example, the following does not work:

A a = dataService.getA(); // gets exisiting A from DB
a.setSubject("My Subject");
B b = dataService.getB(); // gets existing B from DB
b.setFoo(123.45);
insertUpdate(a, b, false);

Here is entity class A:

@Entity
@Table(name = "A")
@SuppressWarnings("unused")
public class A implements Serializable {

    /**
     * Method called by container before DB insert.
     */
    @PrePersist
    public void beforeInsert() {
        if (objectType == null) {
            objectType = ObjectTypeEnum.UNKNOWN;
        }
    }

    @Id
    @Column(name = "MY_UID")
    private String myUid;

    @Column(name = "MY_NUMBER")
    private int myNumber;

    @Column(name = "SUBJECT")
    private String subject;

    @Enumerated(EnumType.ORDINAL)
    @Column(name = "OBJECT_TYPE")
    private ObjectTypeEnum objectType;

    // other columns, getters, setters, equals, etc.

}

And here is entity class B:

@Entity
@Table(name = "B")
@SuppressWarnings("unused")
public class B implements Serializable {

    @Id
    @Column(name = "MY_UID", nullable = false)
    private String myUid;

    @Column(name = "FOO")
    private double foo;

    @Column(name = "BAR")
    private double bar;
}

Note: MY_UID in B is a foreign key for MY_UID in A.

Note: There is no updatable=false property on any of the columns I'm trying to update.

Let me know if there are any other files/methods/etc. I can include or if there is any more information I can provide. It is also worth noting that I do not maintain any of the code I posted, but I've been working with it recently. I asked the maintainer, and he said he wasn't sure why this was happening. Thanks in advance for any help!

EDIT - Here is the datasources configuration from standalone-full.xml:

<subsystem xmlns="urn:jboss:domain:datasources:3.0">
        <datasources>
            <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
                <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
                <driver>h2</driver>
                <security>
                    <user-name>sa</user-name>
                    <password>****</password>
                </security>
            </datasource>
            <datasource jta="true" jndi-name="java:jboss/datasources/SFDS" pool-name="SFDS" enabled="true" use-java-context="true" use-ccm="true">
                <connection-url>jdbc:oracle:thin:@DB:1521:ORACLE_SID</connection-url>
                <driver>ojdbc6</driver>
                <new-connection-sql>select 1 from dual</new-connection-sql>
                <pool>
                    <min-pool-size>10</min-pool-size>
                    <max-pool-size>100</max-pool-size>
                    <prefill>false</prefill>
                    <use-strict-min>false</use-strict-min>
                    <flush-strategy>FailingConnectionOnly</flush-strategy>
                </pool>
                <security>
                    <user-name>wildfly_user</user-name>
                    <password>****</password>
                </security>
                <validation>
                    <check-valid-connection-sql>select 1 from dual</check-valid-connection-sql>
                    <use-fast-fail>true</use-fast-fail>
                </validation>
            </datasource>
            <drivers>
                <driver name="h2" module="com.h2database.h2">
                    <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
                </driver>
                <driver name="ojdbc6" module="com.oracle.jdbc">
                    <xa-datasource-class>oracle.jdbc.OracleDriver</xa-datasource-class>
                </driver>
            </drivers>
        </datasources>
    </subsystem>

Solution

  • It seems that the issue was that before trying to call merge, we were calling a SELECT query with a read-only hint. Removing this hint allow the merge to show up in the DB successfully. Thanks Chris for the help!