All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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 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 table) throws SQLException {
		for (final String sql : this.indexs(table)) {
			con.createStatement().execute(sql);
		}
	}

	@SafeVarargs
	final public FlipDB createTables(final Class... tables) {
		try {
			Connection con = null;
			try {
				con = DriverManager.getConnection(this.url);
				for (final Class 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 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 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