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.sql.Timestamp;
12 import java.util.ArrayList;
13 import java.util.Date;
14 import java.util.List;
16 import net.jaekl.cfb.db.Column;
17 import net.jaekl.cfb.db.Column.Type;
18 import net.jaekl.cfb.db.Condition;
19 import net.jaekl.cfb.db.Row;
20 import net.jaekl.cfb.db.Sequence;
21 import net.jaekl.cfb.db.Table;
23 public abstract class DbDriver {
24 static int PENDING_LIMIT = 1024; // Rough limit at which point we'll start a new batch for batch updates
30 // Load the JDBC driver
31 public abstract void load() throws ClassNotFoundException;
33 public abstract Connection connect(String host, int port, String dbName, String user, String pass) throws SQLException;
35 public boolean createTable(Connection con, Table table) throws SQLException
37 String sql = createTableSql(table);
38 try (PreparedStatement ps = con.prepareStatement(sql)) {
41 catch (SQLException exc) {
42 throw new SQLException("Failed to executeUpdate: " + sql, exc);
48 public void dropTable(Connection con, Table table) throws SQLException
50 String sql = dropTableSql(table);
51 try (PreparedStatement ps = con.prepareStatement(sql)) {
54 catch (SQLException exc) {
55 throw new SQLException("Failed to drop table: " + sql, exc);
59 public boolean createSequence(Connection con, Sequence seq) throws SQLException
61 String sql = createSequenceSql(seq);
62 try (PreparedStatement ps = con.prepareStatement(sql)) {
65 catch (SQLException exc) {
66 throw new SQLException("Failed to executeUpdate: " + sql, exc);
72 public void dropSequence(Connection con, Sequence seq) throws SQLException
74 String sql = dropSequenceSql(seq);
75 try (PreparedStatement ps = con.prepareStatement(sql)) {
78 catch (SQLException exc) {
79 throw new SQLException("Failed to drop sequence: " + sql, exc);
83 public List<Row> select(Connection con, Column[] columns, Table[] tables, Condition[] conditions)
86 String sql = selectSql(columns, tables, conditions);
87 ArrayList<Row> result = new ArrayList<Row>();
89 try (PreparedStatement ps = con.prepareStatement(sql)) {
91 for (Condition condition : conditions) {
92 if (condition.getOperation().hasParam()) {
94 ps.setObject(index, condition.getValue());
98 try (ResultSet rs = ps.executeQuery()) {
100 Object[] values = new Object[columns.length];
101 for (index = 0; index < columns.length; ++index) {
102 values[index] = rs.getObject(index + 1);
104 Row row = new Row(columns, values);
113 // Returns the number of rows inserted
114 public int insert(Connection con, Table table, Object[][] values) throws SQLException
117 int pendingValues = 0;
119 String sql = insertSql(table);
121 try (PreparedStatement ps = con.prepareStatement(sql))
123 for (int row = 0; row < values.length; ++row) {
124 Object[] data = values[row];
126 assert(null != data);
127 assert(data.length == table.getNumColumns());
129 for (int col = 0; col < data.length; ++col) {
130 Object obj = data[col];
131 if (obj instanceof java.util.Date) {
132 Date date = (Date)obj;
133 Timestamp ts = new Timestamp(date.getTime());
134 ps.setTimestamp(col + 1, ts);
137 ps.setObject(col + 1, data[col]);
143 int rowsFlushed = checkFlushBatch(ps, pendingValues, false);
144 if (rowsFlushed > 0) {
145 count += rowsFlushed;
150 count += checkFlushBatch(ps, pendingValues, true);
156 public long nextVal(Connection con, Sequence seq) throws SQLException
158 String sql = nextValSql(seq);
160 try (PreparedStatement ps = con.prepareStatement(sql))
162 try (ResultSet rs = ps.executeQuery()) {
164 return rs.getLong(1);
169 throw new SQLException("No value returned for sequence: " + sql);
172 int checkFlushBatch(PreparedStatement ps, int pendingValues, boolean forceFlush) throws SQLException
176 if (forceFlush || (pendingValues >= PENDING_LIMIT))
178 int[] updateCounts = ps.executeBatch();
179 for (int i = 0; i < updateCounts.length; ++i) {
180 if (updateCounts[i] > 0) {
181 count += updateCounts[i];
189 String insertSql(Table table) {
190 StringBuilder sb = new StringBuilder("INSERT INTO ");
191 sb.append(table.getName())
192 .append(" VALUES (");
194 for (int i = 0; i < table.getNumColumns(); ++i) {
202 return sb.toString();
205 protected String selectSql(Column[] columns, Table[] tables, Condition[] conditions)
207 StringBuilder sb = new StringBuilder("SELECT ");
209 boolean firstColumn = true;
210 for (Column column : columns) {
217 sb.append(column.getName());
222 boolean firstTable = true;
223 for (Table table : tables) {
230 sb.append(table.getName());
233 if (null != conditions && conditions.length > 0) {
234 sb.append(" WHERE ");
236 boolean firstCondition = true;
238 for (Condition condition : conditions) {
239 if (firstCondition) {
240 firstCondition = false;
246 sb.append(condition.getColumn().getName())
247 .append(condition.getOperation().getSql());
251 return sb.toString();
254 protected String typeName(Type type) {
255 return type.toString();
258 protected String createColumnSql(Column column)
260 String result = column.getName() + " " + typeName(column.getType());
261 if (column.getWidth() > 0) {
262 result += "(" + column.getWidth() + ")";
265 if (NOT_NULL == column.getNull()) {
266 result += " NOT NULL";
275 protected String createTableSql(Table table)
277 assert(null != table);
278 assert(null != table.getName());
279 assert(table.getNumColumns() > 0);
281 StringBuilder sb = new StringBuilder();
283 sb.append("CREATE TABLE ")
284 .append(table.getName())
287 for (int idx = 0; idx < table.getNumColumns(); ++idx) {
291 sb.append(createColumnSql(table.getColumn(idx)));
296 return sb.toString();
299 protected String dropTableSql(Table table) {
300 assert(null != table);
301 assert(null != table.getName());
303 return "DROP TABLE " + table.getName();
306 protected String createSequenceSql(Sequence seq) {
308 assert(null != seq.getName());
310 return "CREATE SEQUENCE " + seq.getName();
313 protected String dropSequenceSql(Sequence seq) {
315 assert(null != seq.getName());
317 return "DROP SEQUENCE " + seq.getName();
320 abstract protected String nextValSql(Sequence seq);