
com.github.gkutiel.flip.db.FlipDB Maven / Gradle / Ivy
package com.github.gkutiel.flip.db;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.github.gkutiel.flip.db.Indexs.Index;
import com.github.gkutiel.flip.utils.Utils;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
public class FlipDB {
static class Magic implements InvocationHandler {
private final List pool = new LinkedList<>();
private final String url;
public Magic(final String url) {
this.url = url;
}
private PreparedStatement buildStmt(final PreparedStatement ps, final Object[] args) {
if (args == null) { return ps; }
for (int i = 0; i < args.length; i++) {
final int index = i + 1;
accept(args[i], new ObjectVisitor() {
@Override
public void visit(final byte[] bs) {
try {
ps.setBinaryStream(index, new ByteInputStream(bs, bs.length));
} catch (final SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void visit(final Double d) {
try {
ps.setDouble(index, d);
} catch (final SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void visit(final Integer i) {
try {
ps.setInt(index, i);
} catch (final SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void visit(final Long l) {
try {
ps.setLong(index, l);
} catch (final SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void visit(final String s) {
try {
ps.setString(index, s);
} catch (final SQLException e) {
throw new RuntimeException(e);
}
}
});
}
return ps;
}
private Connection getConnection() throws SQLException {
synchronized (this.pool) {
if (this.pool.isEmpty()) { return DriverManager.getConnection(this.url); }
return this.pool.remove(0);
}
}
@Override
public Object invoke(final Object proxy, final Method m, final Object[] args) {
Connection con = null;
try {
con = this.getConnection();
final PreparedStatement ps = this.buildStmt(con.prepareStatement(m.getAnnotation(Query.class).value()), args);
final Class> returnType = m.getReturnType();
if (returnType.equals(void.class)) {
ps.executeUpdate();
return null;
}
if (!returnType.equals(ResultSet.class)) { throw new IllegalArgumentException("return type must be void or ResultSet. Found " + returnType); }
return ps.executeQuery();
} catch (final SQLException e) {
throw new RuntimeException(e);
} finally {
this.releseConnection(con);
}
}
private void releseConnection(final Connection con) {
synchronized (this.pool) {
this.pool.add(con);
}
}
}
interface ObjectVisitor {
void visit(byte[] obj);
void visit(Double d);
void visit(Integer i);
void visit(Long l);
void visit(String s);
}
private static final Map, String> JAVA_TO_SQL = new HashMap, String>() {
{
this.put(int.class, "INTEGER");
this.put(Integer.class, "INTEGER");
this.put(long.class, "BIGINT");
this.put(Long.class, "BIGINT");
this.put(double.class, "DOUBLE");
this.put(Double.class, "DOUBLE");
this.put(String.class, "VARCHAR");
this.put(byte[].class, "BLOB");
}
};
static void accept(final Object obj, final ObjectVisitor visitor) {
if (obj instanceof Integer) {
visitor.visit((Integer) obj);
} else if (obj instanceof Long) {
visitor.visit((Long) obj);
} else if (obj instanceof Double) {
visitor.visit((Double) obj);
} else if (obj instanceof String) {
visitor.visit((String) obj);
} else if (obj instanceof byte[]) {
visitor.visit((byte[]) obj);
} else {
throw new UnsupportedOperationException("Unsupported object type: " + obj.getClass());
}
}
private final String url;
public FlipDB(@SuppressWarnings("unused") final Driver driver, final String url) {
this.url = url;
}
private String colDef(final Field col) {
final StringBuilder colDef = new StringBuilder();
colDef.append(col.getName());
colDef.append(" ");
colDef.append(FlipDB.JAVA_TO_SQL.get(col.getType()));
return colDef.toString();
}
private String colDefs(final Class extends Table> table) {
final StringBuilder colDefs = new StringBuilder();
final Field[] cols = table.getDeclaredFields();
colDefs.append(this.colDef(cols[0]));
for (int i = 1; i < cols.length; i++) {
colDefs.append(",");
colDefs.append(this.colDef(cols[i]));
}
return colDefs.toString();
}
private void createIndexes(final Connection con, final Class extends Table> table) throws SQLException {
for (final String sql : this.indexs(table)) {
con.createStatement().execute(sql);
}
}
@SafeVarargs
final public FlipDB createTables(final Class extends Table>... tables) {
try {
Connection con = null;
try {
con = DriverManager.getConnection(this.url);
for (final Class extends Table> table : tables) {
con.createStatement().execute(this.createTableSql(table));
this.createIndexes(con, table);
}
} finally {
if (con != null) {
con.close();
}
}
} catch (final SQLException e) {
throw new RuntimeException(e);
}
return this;
}
private String createTableSql(final Class extends Table> table) {
final StringBuilder sql = new StringBuilder();
sql.append("CREATE TABLE IF NOT EXISTS ");
sql.append(table.getSimpleName());
sql.append("(");
sql.append(this.colDefs(table));
sql.append(")");
return sql.toString();
}
@SuppressWarnings("unchecked")
public T getImplementation(final Class forInterface) {
return (T) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class>[] { forInterface }, new Magic(this.url));
}
private List indexs(final Class extends Table> table) {
final Indexs indexs = table.getAnnotation(Indexs.class);
if (indexs == null) { return Collections.emptyList(); }
final Index[] is = indexs.value();
if (is == null) { return Collections.emptyList(); }
final List sqls = new LinkedList<>();
for (final Index index : is) {
sqls.add("CREATE INDEX ON " + table.getSimpleName() + "(" + Utils.Strings.concat(",", index.value()) + ")");
}
return sqls;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy