1 package net.jaekl.cfb.db.driver;
3 // Copyright (C) 2015 Christian Jaekl
5 import static net.jaekl.cfb.db.Column.Null.*;
7 import java.sql.Connection;
8 import java.sql.PreparedStatement;
9 import java.sql.ResultSet;
10 import java.sql.SQLException;
11 import java.util.ArrayList;
12 import java.util.List;
14 import net.jaekl.cfb.db.Column;
15 import net.jaekl.cfb.db.Column.Type;
16 import net.jaekl.cfb.db.Condition;
17 import net.jaekl.cfb.db.Row;
18 import net.jaekl.cfb.db.Sequence;
19 import net.jaekl.cfb.db.Table;
21 public abstract class DbDriver {
22 static int PENDING_LIMIT = 1024; // Rough limit at which point we'll start a new batch for batch updates
28 // Load the JDBC driver
29 public abstract void load() throws ClassNotFoundException;
31 public abstract Connection connect(String host, int port, String dbName, String user, String pass) throws SQLException;
33 public boolean createTable(Connection con, Table table) throws SQLException
35 String sql = createTableSql(table);
36 try (PreparedStatement ps = con.prepareStatement(sql)) {
39 catch (SQLException exc) {
40 throw new SQLException("Failed to executeUpdate: " + sql, exc);
46 public void dropTable(Connection con, Table table) throws SQLException
48 String sql = dropTableSql(table);
49 try (PreparedStatement ps = con.prepareStatement(sql)) {
52 catch (SQLException exc) {
53 throw new SQLException("Failed to drop table: " + sql, exc);
57 public boolean createSequence(Connection con, Sequence seq) throws SQLException
59 String sql = createSequenceSql(seq);
60 try (PreparedStatement ps = con.prepareStatement(sql)) {
63 catch (SQLException exc) {
64 throw new SQLException("Failed to executeUpdate: " + sql, exc);
70 public void dropSequence(Connection con, Sequence seq) throws SQLException
72 String sql = dropSequenceSql(seq);
73 try (PreparedStatement ps = con.prepareStatement(sql)) {
76 catch (SQLException exc) {
77 throw new SQLException("Failed to drop sequence: " + sql, exc);
81 public List<Row> select(Connection con, Column[] columns, Table[] tables, Condition[] conditions)
84 String sql = selectSql(columns, tables, conditions);
85 ArrayList<Row> result = new ArrayList<Row>();
87 try (PreparedStatement ps = con.prepareStatement(sql)) {
89 for (Condition condition : conditions) {
90 if (condition.getOperation().hasParam()) {
92 ps.setObject(index, condition.getValue());
96 try (ResultSet rs = ps.executeQuery()) {
98 Object[] values = new Object[columns.length];
99 for (index = 0; index < columns.length; ++index) {
100 values[index] = rs.getObject(index + 1);
102 Row row = new Row(columns, values);
111 // Returns the number of rows inserted
112 public int insert(Connection con, Table table, Object[][] values) throws SQLException
115 int pendingValues = 0;
117 String sql = insertSql(table);
119 try (PreparedStatement ps = con.prepareStatement(sql))
121 for (int row = 0; row < values.length; ++row) {
122 Object[] data = values[row];
124 assert(null != data);
125 assert(data.length == table.getNumColumns());
127 for (int col = 0; col < data.length; ++col) {
128 ps.setObject(col + 1, data[col]);
133 int rowsFlushed = checkFlushBatch(ps, pendingValues, false);
134 if (rowsFlushed > 0) {
135 count += rowsFlushed;
140 count += checkFlushBatch(ps, pendingValues, true);
146 public long nextVal(Connection con, Sequence seq) throws SQLException
148 String sql = nextValSql(seq);
150 try (PreparedStatement ps = con.prepareStatement(sql))
152 try (ResultSet rs = ps.executeQuery()) {
154 return rs.getLong(1);
159 throw new SQLException("No value returned for sequence: " + sql);
162 int checkFlushBatch(PreparedStatement ps, int pendingValues, boolean forceFlush) throws SQLException
166 if (forceFlush || (pendingValues >= PENDING_LIMIT))
168 int[] updateCounts = ps.executeBatch();
169 for (int i = 0; i < updateCounts.length; ++i) {
170 if (updateCounts[i] > 0) {
171 count += updateCounts[i];
179 String insertSql(Table table) {
180 StringBuilder sb = new StringBuilder("INSERT INTO ");
181 sb.append(table.getName())
182 .append(" VALUES (");
184 for (int i = 0; i < table.getNumColumns(); ++i) {
192 return sb.toString();
195 protected String selectSql(Column[] columns, Table[] tables, Condition[] conditions)
197 StringBuilder sb = new StringBuilder("SELECT ");
199 boolean firstColumn = true;
200 for (Column column : columns) {
207 sb.append(column.getName());
212 boolean firstTable = true;
213 for (Table table : tables) {
220 sb.append(table.getName());
223 if (null != conditions && conditions.length > 0) {
224 sb.append(" WHERE ");
226 boolean firstCondition = true;
228 for (Condition condition : conditions) {
229 if (firstCondition) {
230 firstCondition = false;
236 sb.append(condition.getColumn().getName())
237 .append(condition.getOperation().getSql());
241 return sb.toString();
244 protected String typeName(Type type) {
245 return type.toString();
248 protected String createColumnSql(Column column)
250 String result = column.getName() + " " + typeName(column.getType());
251 if (column.getWidth() > 0) {
252 result += "(" + column.getWidth() + ")";
255 if (NOT_NULL == column.getNull()) {
256 result += " NOT NULL";
265 protected String createTableSql(Table table)
267 assert(null != table);
268 assert(null != table.getName());
269 assert(table.getNumColumns() > 0);
271 StringBuilder sb = new StringBuilder();
273 sb.append("CREATE TABLE ")
274 .append(table.getName())
277 for (int idx = 0; idx < table.getNumColumns(); ++idx) {
281 sb.append(createColumnSql(table.getColumn(idx)));
286 return sb.toString();
289 protected String dropTableSql(Table table) {
290 assert(null != table);
291 assert(null != table.getName());
293 return "DROP TABLE " + table.getName();
296 protected String createSequenceSql(Sequence seq) {
298 assert(null != seq.getName());
300 return "CREATE SEQUENCE " + seq.getName();
303 protected String dropSequenceSql(Sequence seq) {
305 assert(null != seq.getName());
307 return "DROP SEQUENCE " + seq.getName();
310 abstract protected String nextValSql(Sequence seq);