Hi. I am using a standalone Derby server that comes with my JDK (the
following is invoked in $JDK_HOME/db/bin):

$ ./sysinfo
------------------ Java Information ------------------
Java Version:    1.7.0_07
Java Vendor:     Oracle Corporation
Java home:       /usr/lib/jvm/jdk1.7.0_07/jre
Java classpath:
 
/usr/lib/jvm/java/db/lib/derby.jar:/usr/lib/jvm/java/db/lib/derbynet.jar:/usr/lib/jvm/java/db/lib/derbytools.jar:/usr/lib/jvm/java/db/lib/derbyclient.jar
OS name:         Linux
OS architecture: amd64
OS version:      3.0.0-31-generic
Java user name:  wujek
Java user home:  /home/wujek
Java user dir:   /usr/lib/jvm/jdk1.7.0_07/db/bin
java.specification.name: Java Platform API Specification
java.specification.version: 1.7
java.runtime.version: 1.7.0_07-b10
--------- Derby Information --------
JRE - JDBC: Java SE 7 - JDBC 4.0
[/usr/lib/jvm/jdk1.7.0_07/db/lib/derby.jar] 10.8.2.2 - (1181258)
[/usr/lib/jvm/jdk1.7.0_07/db/lib/derbytools.jar] 10.8.2.2 - (1181258)
[/usr/lib/jvm/jdk1.7.0_07/db/lib/derbynet.jar] 10.8.2.2 - (1181258)
[/usr/lib/jvm/jdk1.7.0_07/db/lib/derbyclient.jar] 10.8.2.2 - (1181258)


The data source configuration is in the attached domain.xml file, but just
for reference:

    <jdbc-connection-pool is-isolation-level-guaranteed="false"
datasource-classname="org.apache.derby.jdbc.ClientXADataSource40"
res-type="javax.sql.XADataSource" name="DerbyTestXAPool">
      <property name="databaseName" value="hibertest" />
      <property name="password" value="hibertest" />
      <property name="user" value="hibertest" />
      <property name="serverName" value="localhost" />
    </jdbc-connection-pool>
    <jdbc-resource pool-name="DerbyTestXAPool" description=""
jndi-name="jdbc/DerbyTestXAPool" />

(So, I am using an XA data source. The DDL script for this database will be
attached.)

The whole project consists of a single 3.0 servlet, whose code is below
(sans package and imports):

@WebServlet(name = "JdbcServlet", value = "/jdbc")
public class JdbcServlet extends HttpServlet {

    @Resource(name = "jdbc/DerbyTestXAPool")
    private DataSource dataSource;

    @Resource
    private UserTransaction utx;

    protected void doGet(HttpServletRequest request, HttpServletResponse
response) throws ServletException, IOException {
        response.setContentType("text/plain");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        Writer out = response.getWriter();
        Connection conn = null;
        try {
//            utx.begin();
            out.write(utx.getStatus() + "\n");
            conn = dataSource.getConnection();
            out.write(conn.getAutoCommit() + "\n");
            conn.setAutoCommit(false);
            out.write(conn.getAutoCommit() + "\n");
            ResultSet rs = conn.createStatement().executeQuery("select
max(id) from person");
            rs.next();
            long nextid = rs.getLong(1) + 1;

            out.write(conn.getAutoCommit() + "\n");
            PreparedStatement pst = conn.prepareStatement("insert into
person(id, first_name, last_name) values(?, ?, ?)");
            out.write(conn.getAutoCommit() + "\n");
            pst.setLong(1, nextid);
            pst.setString(2, "fn" + nextid);
            pst.setString(3, "ln" + nextid);
            pst.execute();
            out.write(conn.getAutoCommit() + "\n");

            rs = conn.createStatement().executeQuery("select id,
first_name, last_name from person");
            while (rs.next()) {
                long id = rs.getLong("id");
                String firstName = rs.getString("first_name");
                String lastName = rs.getString("last_name");
                out.write(String.format("[%d] %s %s\n", id, firstName,
lastName));
            }
//            utx.commit();
            out.write(utx.getStatus() + "\n");
        } catch (Exception e) {
//            try {
//                utx.rollback();
//            } catch (SystemException e2) {
//                ignore
//            }
            throw new ServletException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    // ignore
                }
            }
        }
    }
}

It does this:
1. takes a connection from the data source (which should be an XA
connection per configuration)
2. _does not_ start any transaction, prints the status twice (and I get 6,
which is Status.STATUS_NO_TRANSACTION)
3. sets autoCommit to false and prints it a few times
4. gets the biggest id in the table (0 when table is empty)
5. creates a new row with a prepared statement
6. prints out all rows
7. _does not_ commit the transaction in any way

The output I see is:
6
true
false
false
false
false
[1] fn1 ln1
6

(the 6 is the mentioned utx status, true is the initial value of
autoCommit, and false are the values after setting it to false)

What I don't understand:
1. how is it possible that the insertion is visible in the next select?
there is no transaction, autoCommit is explicitly set to false, there is no
caching like EntityManager cache - or is there?
2. how is it possible that the insertion is made persistent after the code
executes, without the transaction ever being committed (either local or
global) - I can see in a db viewer that the row has been added? the close()
in finally seems to be doing magic things

The transaction isolation is READ_COMITTED by default, at least that's what
I found, so reading the just-inserted row should not happen. I think the RA
for Derby is allowed to start a local transaction when there is no global
one (the specs allows this as optional), but there is not a single commit
in this code. How come the row gets saved?

I am obviously doing things wrong here, making invalid assumptions or just
plain don't know how jdbc / jta works. Could someone help me understand
what is going on here?

wujek

Attachment: test.tar.gz
Description: GNU Zip compressed data

Reply via email to