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

com.github.gkutiel.store.Store Maven / Gradle / Ivy

The newest version!
package com.github.gkutiel.store;

import static java.lang.Integer.MAX_VALUE;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.h2.Driver;

import com.github.gkutiel.store.ConnectionPool.Connection;
import com.google.gson.Gson;

public class Store {
	@Target(ElementType.FIELD)
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface Index {}

	@Target(ElementType.TYPE)
	@Retention(RetentionPolicy.RUNTIME)
	public static @interface Key {
		String[] value();
	}

	private static final Gson GSON = new Gson();
	private static final Logger LOGGER = Logger.getLogger(Store.class.getCanonicalName());
	private static final int DEFAULT_MAX_OBJ_SIZE = 1024;

	public static  Store create(final Class c) {
		return create(c, DEFAULT_MAX_OBJ_SIZE);
	}

	public static  Store create(final Class c, final int maxObjSize) {
		return new Store<>(c, maxObjSize);
	}

	private static  List getIndexableFields(final Class c) {
		final List indexedFields = new ArrayList<>();

		final Field[] declaredFields = c.getDeclaredFields();
		AccessibleObject.setAccessible(declaredFields, true);

		for (final Field field : declaredFields)
			if (field.isAnnotationPresent(Index.class)) indexedFields.add(field);

		return indexedFields;
	}

	private final ConnectionPool pool;
	private final Class clazz;
	private final List indexableFields;
	private final String tableName;
	private final String insert;
	private final String delete;

	@SuppressWarnings("unused")
	private Store(final Class c, final int maxObjSize) {
		this.clazz = c;
		new Driver();

		indexableFields = getIndexableFields(c);

		tableName = clazz.getSimpleName();
		insert = Temp.insert(clazz.isAnnotationPresent(Key.class), tableName, indexableFields.size());
		delete = "DELETE FROM " + tableName + " WHERE ";

		pool = new ConnectionPool("jdbc:h2:db/store");

		createTable(maxObjSize);
		createIndexes();
	}

	public int count() {
		return count("true");
	}

	@SuppressWarnings("resource")
	public int count(final String where, final Object... args) {
		try (Connection con = pool.get()) {

			final ResultSet rs = getCount(con, where, args).executeQuery();
			rs.next();
			return rs.getInt(1);

		} catch (final SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void del(final String where, final Object... args) {
		try (Connection con = pool.get()) {
			getDelete(con, where, args).executeUpdate();
		} catch (final SQLException e) {
			throw new RuntimeException(e);
		}
	}

	@SuppressWarnings("resource")
	public T first(final String where, final Object... args) {
		try (Connection con = pool.get()) {
			final ResultSet rs = getSelect(con, where, 1, args).executeQuery();
			if (!rs.next()) return null;
			return GSON.fromJson(rs.getString(1), clazz);
		} catch (final SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public List get() {
		return get("true");
	}

	@SuppressWarnings("resource")
	public List get(final String where, final Object... args) {
		try (Connection con = pool.get()) {
			final List ts = new ArrayList<>();

			final ResultSet rs = getSelect(con, where, args).executeQuery();
			while (rs.next())
				ts.add(GSON.fromJson(rs.getString(1), clazz));

			return ts;
		} catch (final SQLException e) {
			throw new RuntimeException(e);
		}
	}

	public void put(final T t) {
		try (Connection con = pool.get()) {
			getInsertStmt(con, t).executeUpdate();
		} catch (final Exception e) {
			throw new RuntimeException(e);
		}
	}

	private void createIndex(final Connection con, final Field field) {
		try {
			con.prepareStatement(getCreateIndex(field)).execute();
		} catch (final SQLException e) {
			throw new RuntimeException();
		}
	}

	private void createIndexes() {
		try (Connection con = pool.get()) {
			for (final Field field : indexableFields)
				createIndex(con, field);
		}
	}

	private void createTable(final int maxObjSize) {
		try (Connection con = pool.get()) {
			final String table = Temp.table(tableName, indexableFields, getPrimaryKey(clazz), maxObjSize);
			LOGGER.fine(table);

			con.createStatement().execute(table);

		} catch (final SQLException e) {
			throw new RuntimeException(e);
		}
	}

	private PreparedStatement getCount(final Connection con, final String cond, final Object... args) {
		final PreparedStatement ps = con.prepareStatement("SELECT COUNT(_) FROM " + clazz.getSimpleName() + " WHERE " + cond);
		int i = 1;
		for (final Object v : args)
			Utils.set(ps, i++, v);
		return ps;
	}

	private String getCreateIndex(final Field field) {
		final String createIndex = "CREATE INDEX  IF NOT EXISTS " + field.getName() + " ON " + tableName + "(" + field.getName() + ")";
		LOGGER.fine(createIndex);
		return createIndex;
	}

	private PreparedStatement getDelete(final Connection con, final String cond, final Object... args) {
		final PreparedStatement ps = con.prepareStatement(delete + cond);

		int i = 1;
		for (final Object v : args)
			Utils.set(ps, i++, v);

		LOGGER.fine(ps.toString());
		return ps;
	}

	private PreparedStatement getInsertStmt(final Connection con, final T t) throws IllegalAccessException, SQLException {
		final PreparedStatement ps = con.prepareStatement(insert);

		int i = 1;
		ps.setString(i, GSON.toJson(t));
		for (final Field field : indexableFields)
			Utils.set(ps, ++i, field.get(t));

		LOGGER.fine(ps.toString());
		return ps;
	}

	private String[] getPrimaryKey(final Class c) {
		if (c.isAnnotationPresent(Key.class)) return c.getAnnotation(Key.class).value();
		return null;
	}

	private PreparedStatement getSelect(final Connection con, final String cond, final int limit, final Object... args) {
		final PreparedStatement ps = con.prepareStatement("SELECT _ FROM " + clazz.getSimpleName() + " WHERE " + cond + " LIMIT " + limit);
		int i = 1;
		for (final Object v : args)
			Utils.set(ps, i++, v);
		return ps;
	}

	private PreparedStatement getSelect(final Connection con, final String cond, final Object... args) {
		return getSelect(con, cond, MAX_VALUE, args);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy