0

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>
Evan LaHurd
  • 977
  • 2
  • 14
  • 27
  • In your example, how is the ID being set? Are you trying to insert an A and B instance with null IDs using merge? If B's ID is a foreign key to A, how is it set? – Chris Sep 03 '15 at 14:53
  • Sorry, I edited my example leading up to the merge(). It gets an existing A and B from the DB that has it's MY_UID field set already. For example, say the tables are empty. The first call to the program would create a new A and B, then call `a.setMyUid(UUID.randomUUID().toString());`, `b.setMyUid(a.getMyUid());`, and whatever updates were passed in. Now they are set. Then the next call would get the existing A and B, set some new fields, then try the merge. – Evan LaHurd Sep 03 '15 at 16:38
  • I would also have a look at datasource configuration in WildFly. If you use container's connection pool. What database and jdbc driver are used for datasource. It depends on how you run WildFly, usually standalone.xml is the file of interest – Sasa7812 Sep 03 '15 at 16:47
  • I've included the datasources configuration in the question. See my edit. – Evan LaHurd Sep 03 '15 at 18:01
  • 1
    What does the getA look like? The only thing I can see that would cause this is if you are some how changing the object in the shared cache, so that when the changed object is merged, Eclipselink does not see that there are changes. Make sure that your query for A and B aren't trying to get read-only instances or an optimization that might cause it to return a read-only instance from the shared cache. Try explicitly calling em.find before the merge and comparing what is returned from the find to what you have in your entity. – Chris Sep 03 '15 at 22:38
  • Removing that hint worked! Not sure what side-effects it will have on other code, but I'll follow up with our DB lead on that. Thanks for the help! I'd accept your answer if you had one :P – Evan LaHurd Sep 04 '15 at 22:19

1 Answers1

0

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!

Evan LaHurd
  • 977
  • 2
  • 14
  • 27