8 Ocak 2017 Pazar

JDBC Statement Arayüzü

Giriş
Şu satırı dahil ederiz.
import java.sql.Statement;
constructor
Bir connection nesnesi ile yaratılır.
Connection connection = DriverManager.getConnection(...);
Statement stmt = connection.createStatement();
execute metodu - Her Hangi Bir SQL
Açıklaması şöyle
This method is more general and can be used to execute any SQL statement, including both queries and updates (INSERT, UPDATE, DELETE). It returns a boolean value indicating whether the first result is a ResultSet (true) or an update count (false). If the first result is a ResultSet, you can retrieve it using the getResultSet() method.
Örnek
Şöyle yaparız
Statement statement = connection.createStatement();
boolean isResultSet = statement
  .execute("UPDATE mytable SET name = 'New Name' WHERE id = 1");

if (isResultSet) {
    ResultSet resultSet = statement.getResultSet();
    // Process the result set if it exists
} else {
    int updateCount = statement.getUpdateCount();
    // Process the update count
}
statement.close();
executeQuery metodu - SELECT CÜMLESİ
Verilen select cümlesini çalıştırır
Örnek
Şöyle yaparız.
String sql = "SELECT * FROM TimeTable";
ResultSet rs = stmt.executeQuery( sql ))
executeUpdate - UPDATE + CREATE CÜMLESİ
Örnek
Şöyle yaparız
String url = "jdbc:postgresql://localhost/mydb";
String user = "myuser";
String password = "mypassword";

try (Connection connection = DriverManager.getConnection(url, user, password);
     Statement statement = connection.createStatement()) {

  // Create a unique index on the "user_id" field in the "users" table
  String sql = "CREATE UNIQUE INDEX ON users (user_id)";
  statement.executeUpdate(sql);

} catch (SQLException e) {
  ...
}
setFetchSize metodu
Eğer çok büyük bir tablodan veri çekeceksek kullanılır. Çünkü bazen istemci tarafında çok büyük veriyi çekmeye çalışırken bellek yetersizliği yüzünden OOM alınabiliyor. Açıklaması şöyle. Yani JDBC sürücüsü bu değeri dikkate almayabilir
Gives the JDBC driver a hint as to the number of rows that should be fetched from the database when more rows are needed for ResultSet objects generated by this Statement. If the value specified is zero, then the hint is ignored. The default value is zero.
Örnek
Şeklen şöyle. 100 defa next() yaptıktan sonra bir sonraki veri tabanı okuma işlemi gerçekleşir.
Statement stmt = ...;
stmt.setFetchSize(100);
ResultSet rs = ...;
rs.next(); // Triggers fetch of 100 rows, positions on row 1 / 100
...
rs.next(); // Positions on row 100 / 100
rs.next(); // Triggers fetch of 100 rows, positions on row 101 / 200
Örnek - MySQL
MySQL aslında setFetchSize() içine verilen bir sayı çağrısını desteklemiyor. Ancak büyük tabloları okumak için 2 yöntem var

Varsayılan getFetchSize değeri 0

1. Streaming mode - setFetchSize Kullanmak - Row By Row
Bu çağrıda özel bir sayı olan Integer.MIN_VALUE yani eksi bir değeri kullanmak gerekiyor. Satırları teker teker getiriyor. 

Getirisi
Bu yöntem daha az bellek kullanıyor dolayısıyla çok büyük tabloları okumak için kullanılabilir. 

Götürüsü
Veri tabanına daha fazla çağrıda bulunmak gerekiyor.  Yani round trip artıyor.

Örnek
Şöyle yapılabilir
stmt.setFetchSize(Integer.MIN_VALUE);
Örnek
Şöyle yaparız
stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
              java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
Açıklaması şöyle. Eksi bir sayı kullanılması dikkat çekmek için özellikle seçilmiş
So the MySQL only has the option to get everything or to get one at a time. This means that there is no way for a driver to respect a different fetch size. And due to the caveats when getting one-by-one they opted to use the Integer.MIN_VALUE (instead of simply 1) as a signal that you should really think before doing this.
Bu yöntemi kullanırken şunlar dikkat etmek lazım
There are some caveats with this approach. You must read all of the rows in the result set (or close it) before you can issue any other queries on the connection, or an exception will be thrown.
2.  Cursor-based streaming - JDBC bağlantısında useCursorFetch=true Kullanmak

Getirisi
Bu seçenek ile artık setFetchSize() ile belirtilen sayı kullanılıyor. 

Götürüsü
Açıklaması şöyle. Yani sorgunun çalıştırılıp sonucunun geçici bir tabloya kopyalanması şeklinde icra ediliyor.
If you enable the MySQL JDBC option useCursorFetch, fetchSize will indeed be respected by the driver.

However, there is one disadvantage to this approach: It will use server-side cursors, which in MySQL are implemented using temporary tables. This will mean that results will not arrive until the query has been completed on the server, and that additional memory will be used server-side.

If you just want to use result streaming and don't care about the exact fetch size, the overhead of setFetchSize(Integer.MIN_VALUE) is not as bad as the docs might imply. It actually just disables client-side caching of the entire response and gives you responses as they arrive; there is no round-trip per row required.
Kafka Connect için bir örnek burada

Örnek
Şöyle yaparız
conn = DriverManager.getConnection("jdbc:mysql://localhost/?useCursorFetch=true", 
  "user", "s3cr3t");
stmt = conn.createStatement();
stmt.setFetchSize(100);
rs = stmt.executeQuery("SELECT * FROM your_table_here");

Örnek - PostgreSQL
Varsayılan getFetchSize değeri 0

Eğer PostgreSQL ile setFetchSize() yapılmazsa OOM alınabilir. Çünkü varsayılan davranış şöyle
That’s the default behavior: the JDBC ResultSet is not a cursor but a buffer.
Bir başka açıklama şöyle
By default the driver collects all the results for the query at once. This can be inconvenient for large data sets so the JDBC driver provides a means of basing a ResultSet on a database cursor and only fetching a small number of rows.
setFetchSize() kullanırsak Cursor modune geçiyor. Açıklama şöyle
A small number of rows are cached on the client side of the connection and when exhausted the next block of rows is retrieved by repositioning the cursor.
setFetchSize() kullanabilmek için kısıtlar şöyle
Cursor based ResultSets cannot be used in all situations. There a number of restrictions which will make the driver silently fall back to fetching the whole ResultSet at once.

1. The connection to the server must be using the V3 protocol. This is the default for (and is only supported by) server versions 7.4 and later.

2. The Connection must not be in autocommit mode. The backend closes cursors at the end of transactions, so in autocommit mode the backend will have closed the cursor before anything can be fetched from it.

3. The Statement must be created with a ResultSet type of ResultSet.TYPE_FORWARD_ONLY. This is the default, so no code will need to be rewritten to take advantage of this, but it also means that you cannot scroll backwards or otherwise jump around in the ResultSet.

4. The query given must be a single statement, not multiple statements strung together with semicolons.
Yani autocommit (false) olmalı. Şöyle yaparız
// make sure autocommit is off
conn.setAutoCommit(false);
Statement st = conn.createStatement();

// Turn use of the cursor on.
st.setFetchSize(50);
ResultSet rs = st.executeQuery("SELECT * FROM mytable");
while (rs.next()) {
  System.out.print("a row was returned.");
}
rs.close();

// Turn the cursor off.
st.setFetchSize(0);
rs = st.executeQuery("SELECT * FROM mytable");
while (rs.next()) {
  System.out.print("many rows were returned.");
}
rs.close();

// Close the statement.
st.close();
Burada bir örnek var. Açıklaması şöyle
Note that I’ve set AutoCommit to false, and this is required for the PostgreSQL cursor behavior when setting a non-zero fetch size, or it still buffers the whole rows.
Örnek - Oracle
Açıklaması şöyle. Bu bağlantıdaki yazıyı mutlaka okumak lazım
With Oracle, the default JDBC fetch size is 10 rows
Oracle ile her hangi bir kısıt yok, çünkü varsayılan davranış Cursor modu. Şöyle yaparız
int batchSize = 1000;

try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
  String query = "SELECT * FROM your_table";
  Statement statement = connection.createStatement();
  statement.setFetchSize(batchSize);

  ResultSet resultSet = statement.executeQuery(query);

  while (resultSet.next()) {
    // Process the data for the current row
    String column1 = resultSet.getString("column1");
    int column2 = resultSet.getInt("column2");
    // ... Process other columns

    // Perform batch-specific operations
    // ...

    // Optionally, store the processed data or perform other operations
  }

  resultSet.close();
  statement.close();
} catch (SQLException e) {
  ...
}


Hiç yorum yok:

Yorum Gönder