Skip to Main Content

Java Database Connectivity (JDBC)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

java.sql.PreparedStatement returns -2 instead of update counts

1037702Apr 3 2014 — edited Oct 15 2014

Hi everyone.

As noted in the Oracle Database JDBC Developer’s Guide 12c Release 1 (version 12.1), chapter 21:

Note: Starting from Oracle Database 12c Release 1 (12.1), Oracle update batching is deprecated. Oracle recommends that you use standard JDBC batching instead of Oracle update batching.

We tested the standard JDBC batching using Oracle jdbc driver version 12.1.0.1 (ojdbc7-12.1.0.1.jar) and Oracle Database 11g Enterprise Edition Release version 11.2.0.3.0. The test fails in the asserts regarding the row counts returned by the method java.sql.Statement.executeBatch(), i.e., all the items in the returned array are -2 instead of 1 or 0. We suspect this has to do with the version of the database we are using. Should the test pass if the version 12.1 of the database were used instead? In other words, does the standard JDBC batching require Oracle jdbc driver version 12.1 and Oracle Database 12.1 to return the actual update counts in PreparedStatement.executeBatch()?

Following is the test case we used, based on Junit 4.

Hope I've made myself clear.

public class StandardJDBCBatchingTest {

  static String DB_URL = "jdbc:oracle:thin:@db-server:1530:db-port";

  static String DB_USER = "xxx";

  static String DB_PASSWORD = "yyy";

  @Test

  public void test() throws SQLException {

    String tableName = "ORA_JDBC_BATCHING";

    String createTableSql = "create table " + tableName + "( ID integer, NAME varchar(100) )";

    String dropTableSql = "drop table " + tableName + " cascade constraints";

    String insertSql = "insert into " + tableName + "( ID, NAME ) values ( ?, ? )";

    String updateSql = "update " + tableName + " set NAME=? where ID=?";

    Connection conn = null;

    PreparedStatement insertStmt = null;

    PreparedStatement updateStmt = null;

    try {

      conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);

      DatabaseMetaData dbInfo = conn.getMetaData();

      System.out.println("Product version: " + dbInfo.getDatabaseProductVersion());

      conn.setAutoCommit(false);

      try {

        conn.prepareStatement(dropTableSql).executeUpdate();

      } catch (SQLException e) {

        // ignore exception assuming it was thrown because the table doesn't exist

      }

      conn.prepareStatement(createTableSql).executeUpdate();

      insertStmt = conn.prepareStatement(insertSql);

      insertStmt.setLong(1, 1);

      insertStmt.setString(2, "one");

      insertStmt.addBatch();

      insertStmt.setLong(1, 2);

      insertStmt.setString(2, "two");

      insertStmt.addBatch();

      int[] rowCount = insertStmt.executeBatch();

      assertEquals(2, rowCount.length);

      assertEquals(1, rowCount[0]);

      assertEquals(1, rowCount[1]);

      updateStmt = conn.prepareStatement(updateSql);

      updateStmt.setLong(2, 1);

      updateStmt.setString(1, "one.one");

      updateStmt.addBatch();

      updateStmt.setLong(2, 3);

      updateStmt.setString(1, "two.two");

      updateStmt.addBatch();

      rowCount = updateStmt.executeBatch();

      assertEquals(2, rowCount.length);

      assertEquals(1, rowCount[1]);

      assertEquals(0, rowCount[1]);

    } finally {

      if (insertStmt != null)

        insertStmt.close();

      if (updateStmt != null)

        updateStmt.close();

      if (conn != null) {

        conn.commit();

        conn.close();

      }

    }

  }

}

This post has been answered by unknown-7404 on Apr 3 2014
Jump to Answer

Comments

mariam.kupa

Hello,

I have seen the same problem when using fast refresh materialized view that uses mview log also.

Do an explicit  select privilege for the mlog:

grant select on mlog$_<master_table> to <mview_schema>

https://deeparaja.wordpress.com/2012/07/17/materialized-view-fast-refresh-fails-with-ora-12018-ora-00942/

Martin Preiss

thank you for the answer and the link: but in my case the owner of table and mview is the same and the ora-942 is not related to the accessed table, but to an internal object and furthermore a transient object, that cannot be found in the dictionary at all (so the ora-942 makes sense - but not the idea to gather statistics for this object at all).

BobLilly

Hi Martin,

Have you tried the OPT_PARAM('optimizer_dynamic_sampling', '0') hint?

Regards,

Bob

AndrewSayer
Answer

I can replicate on my 12.2 instance, it looks like there's a problem with the lateral view existing in the execution plan. I can't see a no lateral view hint, and I couldn't see anything in the plan outline that looks relevant but you can hint to use an older optimizer environment, 11.1.0.7 did the trick for me (although it was the first I chose).

create materialized view t_mv

as

select /*+OPTIMIZER_FEATURES_ENABLE('11.1.0.7')*/t.col1, t.col2, r.val

  from t,

  LATERAL (SELECT MIN(val) AS val

            FROM JSON_TABLE ( t.col2, '$[*].target'

                              COLUMNS (val NUMBER PATH '$')

                              )

          ) r;

exec dbms_mview.refresh('t_mv','c')

PL/SQL procedure successfully completed.

I suggest you raise an SR as this could effect a lot of other things, and should be relatively painless as you've got an easily reproducible test case

Marked as Answer by Martin Preiss · Sep 27 2020
Donatello Settembrino

trying on a 12.2 with SYS user (disabling query rewrite) it seems to work correctly without modifying OFE

SQL> select user from dual;

USER

------

SYS

SQL> create materialized view t_mv

  2  disable query rewrite

  3  as

  4  select t.col1, t.col2, r.val

  5    from t,

  6    LATERAL (SELECT MIN(val) AS val

  7              FROM JSON_TABLE ( t.col2, '$[*].target'

  8                                COLUMNS (val NUMBER PATH '$')

  9                                )

10            ) r;

Creata vista materializzata.

SQL> exec dbms_mview.refresh('T_MV');

Procedura PL/SQL completata correttamente.

Mustafa KALAYCI

Hi Martin,

I have the same result but if you change the query as a subquery it works. of course mv does not support scalar subqueries so I created a view to achive that:

create or replace view v_x as

select t.col1, t.col2, (SELECT min(val) AS val

                         FROM JSON_TABLE ( t.col2, '$[*].target'

                                           COLUMNS (val NUMBER PATH '$')

                                          )

                       ) as val

from t;

drop materialized view t_mv;

create materialized view t_mv

as

select * from v_x;

exec dbms_mview.refresh('t_mv');

it works and when I checked the execution plan of the view I can see that VW_LAT*** view. I hope that gives some idea.

Martin Preiss

Hi Andrew,

thank you for the hint: I didn't consider this option since the lateral join was a 12c addition (if I recall  this right). This should work in the given context.

Regards

Martin

Martin Preiss

Hi Donatello,

SYS is special: I would not be surprised to see different strategies, when SYS is concerned.

Regards

Martin

Martin Preiss

Hi Mustafa,

thank you for adding this solution, which would also be a valid option.

Regards

Martin

Donatello Settembrino

in fact, I did not indicate it as a solution but it seemed "strange" to me that without changing the behavior of the optimizer (OFE) it would still work under SYS user.

Regards

Martin Preiss

strange indeed, but SYS tends to include strange effects.

AndrewSayer

Dom Brooks wrote:

Would be interesting to know what fix is responsible

https://blog.dbi-services.com/ofe-optimizer-features-enable/

https://blog.tanelpoder.com/posts/scripts-for-drilling-down-into-unknown-optimizer-changes/

I tried the obvious parameter (with lateral in the name) but it wasn't that. I started writing some scripts a while ago to brute force these things with explain plan, but only managed to get as far as it cycling through fix controls (and can confirm there's no fix control in it). Mauro Pagano's Pathfinder (which my scripts mimic) does include hidden parameters but it has to execute the SQL each time and needs to be run as SYS (which doesn't seem to have the problem) https://mauro-pagano.com/category/pathfinder/ . Might be worth kicking off overnight and checking the results in the morning.

1 - 13
Locked Post
New comments cannot be posted to this locked post.

Post Details

Locked on May 2 2014
Added on Apr 3 2014
8 comments
1,467 views