Newer
Older
simple-database / src / nl / astraeus / database / SimpleDatabase.java
package nl.astraeus.database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import nl.astraeus.database.cache.Cache;
import nl.astraeus.database.jdbc.ConnectionProvider;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * User: rnentjes
 * Date: 10-12-16
 * Time: 14:50
 */
public class SimpleDatabase {
    private final static Logger logger = LoggerFactory.getLogger(SimpleDatabase.class);

    protected static Map<String, SimpleDatabase> databases = new ConcurrentHashMap<>();

    public static SimpleDatabase get() {
        return get("default");
    }

    public static SimpleDatabase get(String name) {
        SimpleDatabase result = databases.get(name);

        if (result == null) {
            throw new IllegalStateException("Database with name '"+name+"' is not defined!");
        }

        return result;
    }

    public static SimpleDatabase define(ConnectionProvider provider) {
        return define("default", provider);
    }

    public static SimpleDatabase define(String name, ConnectionProvider provider) {
        SimpleDatabase result = databases.get(name);

        if (result != null) {
            throw new IllegalStateException("Database with name '"+name+"' is already defined!");
        }

        SimpleDatabase database = new SimpleDatabase(name, provider);

        databases.put(name, database);

        return database;
    }

    private ThreadLocal<Transaction> transactions = new ThreadLocal<>();
    private Map<Class<?>, ObjectPersister> objectPersisters = new HashMap<>();
    private ConnectionProvider connectionProvider;
    private Map<Class<?>, MetaData> metaData = new ConcurrentHashMap<>();

    private DdlMapping ddlMapping;
    private Cache cache;
    private final String defineName;

    private boolean executeDDLUpdates;

    private SimpleDatabase(String defineName, ConnectionProvider connectionProvider) {
        this.defineName = defineName;
        this.connectionProvider = connectionProvider;
        this.executeDDLUpdates = false;
        this.ddlMapping = new DdlMapping(connectionProvider.getDefinition());
        this.cache = new Cache();
    }

    public void setExecuteDDLUpdates(boolean executeDDLUpdates) {
        this.executeDDLUpdates = executeDDLUpdates;
    }

    public boolean isExecuteDdlUpdates() {
        return executeDDLUpdates;
    }

    public DdlMapping getDdlMapping() {
        return ddlMapping;
    }

    <T> MetaData<T> getMetaData(Class<T> cls) {
        MetaData result = metaData.get(cls);

        if (result == null) {
            synchronized (SimpleDatabase.class) {
                result = metaData.get(cls);

                if (result == null) {
                    long start = System.nanoTime();

                    result = new MetaData<>(cls, this);

                    logger.info("Getting metadata for class {} took {}ms", cls.getSimpleName(), (System.nanoTime() - start) / 1000000f);

                    metaData.put(cls, result);
                }
            }
        }

        return result;
    }

    protected <T> ObjectPersister<T> getObjectPersister(Class<T> cls) {
        ObjectPersister<T> result = objectPersisters.get(cls);

        if (result == null) {
            result = new ObjectPersister<>(cls, getMetaData(cls), cache);

            objectPersisters.put(cls, result);
        }

        return result;
    }

    public Connection getNewConnection() {
        try {
            return connectionProvider.getConnection();
        } catch(SQLException | ClassNotFoundException e) {
            throw new IllegalStateException(e);
        }
    }

    protected Connection getConnection() {
        if (transactions.get() == null) {
            throw new IllegalStateException("No transaction active!");
        }

        return transactions.get().getConnection();
    }

    public boolean transactionActive() {
        return transactions.get() != null;
    }

    public void begin() {
        if (transactionActive()) {
            throw new IllegalStateException("Transaction active!");
        }
        transactions.set(new Transaction(getNewConnection()));
    }

    public void commit() {
        if (transactions.get() != null) {
            Connection connection = transactions.get().getConnection();
            try {
                connection.commit();
            } catch (SQLException e) {
                throw new IllegalStateException(e);
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                transactions.remove();
            }
        }
    }

    public void rollback() {
        if (transactions.get() != null) {
            Connection connection = transactions.get().getConnection();
            try {
                connection.rollback();
            } catch (SQLException e) {
                throw new IllegalStateException(e);
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                transactions.remove();
            }
        }
    }

    public void execute(String sql, Object ... params) {
        PreparedStatement statement = null;

        try {
            statement = getConnection().prepareStatement(sql);
            int index = 1;

            for (Object param : params) {
                StatementHelper.setStatementParameter(statement, index++, param);
            }

            statement.execute();
        } catch (SQLException e) {
            throw new IllegalStateException(e);
        } finally {
            try {
                if (statement != null && !statement.isClosed()) {
                    statement.close();
                }
            } catch (SQLException e) {
                if (System.err != null) {
                    e.printStackTrace(System.err);
                }
            }
        }
    }

    public ResultSet executeQuery(String sql, Object ... params) {
        PreparedStatement statement = null;

        try {
            statement = getConnection().prepareStatement(sql);
            int index = 1;

            for (Object param : params) {
                StatementHelper.setStatementParameter(statement, index++, param);
            }

            return statement.executeQuery();
        } catch (SQLException e) {
            throw new IllegalStateException(e);
        }
    }

    public int executeUpdate(String sql, Object ... params) {
        PreparedStatement statement = null;

        try {
            statement = getConnection().prepareStatement(sql);
            int index = 1;

            for (Object param : params) {
                StatementHelper.setStatementParameter(statement, index++, param);
            }

            return statement.executeUpdate();
        } catch (SQLException e) {
            throw new IllegalStateException(e);
        } finally {
            try {
                if (statement != null && !statement.isClosed()) {
                    statement.close();
                }
            } catch (SQLException e) {
                if (System.err != null) {
                    e.printStackTrace(System.err);
                }
            }
        }
    }

    public Cache getCache() {
        return cache;
    }

    public void invalidateCache(Class<?> cls) {
        cache.clear(cls);
    }

    public void dispose() {
        connectionProvider.dispose();
        metaData.clear();
        objectPersisters.clear();
        databases.remove(defineName);
    }
}