package com.reflectiondao.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import com.reflectiondao.model.IModel;
import com.reflectiondao.strategy.DaoQueryStrategy;
/**
* This is abstract generic Dao class. When using this in a new project, extend
* this class:
*
* <pre>
* public class ProjectDao<T extends IModel> extends Dao<T> {
*
* private static String connectionType = "jdbc:sqlite";
*
* private static String database = "project.db3";
*
* private static String jdbcClassName = "org.sqlite.JDBC";
*
* protected ProjectDao(Class<T> c) {
* super(c, connectionType, database, jdbcClassName);
* }
* }
* </pre>
*
* This class houses all the generalized create, select, selectAll, etc methods
* that are normally used to talk to the database. If you want to add new base
* methods for your DAOs, just add them to the class that extends this class.
*
* Once this class has been extended, you can then make Dao instances from it.
*
*
* @param <T>
* the generic type that extends IModel
*/
public abstract class Dao<T extends IModel> {
/** A static HashMap of the create statements. Acts as a cache. */
protected static Map<Class<? extends IModel>, String> createStatements = new HashMap<>();
/** A static HashMap of the select statements. Acts as a cache. */
protected static Map<Class<? extends IModel>, String> selectStatements = new HashMap<>();
/** A static HashMap of the select statements. Acts as a cache. */
protected static Map<Class<? extends IModel>, String> deleteStatements = new HashMap<>();
/** A static HashMap of the select statements. Acts as a cache. */
protected static Map<Class<? extends IModel>, String> updateStatements = new HashMap<>();
/** A static HashMap of the select all statements. Acts as a cache. */
protected static Map<Class<? extends IModel>, String> selectAllStatements = new HashMap<>();
/** A static HashMap of the delete all statements. Acts as a cache. */
protected static Map<Class<? extends IModel>, String> deleteAllStatements = new HashMap<>();
/** The data source. */
protected DriverManagerDataSource dataSource = new DriverManagerDataSource();
/** The strat. */
protected DaoQueryStrategy<T> strat = null;
/** The type. */
private Class<T> type;
/**
* Instantiates a new dao.
*
* @param cName
* the c name
* @param connectionType
* the connection type
* @param database
* the database
* @param jdbcClassName
* the jdbc class name
*/
protected Dao(Class<T> cName, String connectionType, String database,
String jdbcClassName) {
this.type = cName;
dataSource.setDriverClassName(jdbcClassName);
dataSource.setUrl(connectionType + ":" + database);
this.strat = new DaoQueryStrategy<T>(cName);
}
/**
* Instantiates a new dao using a DataSource.
*
* @param cName
* the c name
* @param db
* the db
*/
protected Dao(Class<T> cName, DriverManagerDataSource db) {
this.dataSource = db;
this.strat = new DaoQueryStrategy<T>(cName);
}
/**
* Sets the data source.
*
* @param ds
* the new data source
*/
public void setDataSource(DataSource ds) {
this.dataSource = (DriverManagerDataSource) ds;
}
/**
* Creates a instance of T t in the database. This uses Reflection for the
* first usage; but caches the create statement, speeding it up slightly
* after the first run.
*
* @param t
* the t
* @return the t
*/
public T create(final T t) {
JdbcTemplate insert = new JdbcTemplate(dataSource);
final Class<T> type = this.type;
final String createStatement = Dao.createStatements.get(type);
KeyHolder holder = new GeneratedKeyHolder();
try {
// Create a prepared statement.
insert.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(
Connection connection) throws SQLException {
String createStatementToUse = null;
// Use the cached value if we have one, otherwise, generate
// one and cache it.
if (createStatement != null) {
createStatementToUse = createStatement;
} else {
createStatementToUse = strat.generateCreateStatement(t);
Dao.createStatements.put(type, createStatementToUse);
}
PreparedStatement ps = connection.prepareStatement(
createStatementToUse,
Statement.RETURN_GENERATED_KEYS);
strat.prepareStatement(ps, t);
return ps;
}
}, holder);
} catch (Exception e) {
e.printStackTrace();
}
int id = holder.getKey().intValue();
// Get the T that we just inserted.
return this.select(id);
}
/**
* Selects an instance of T from the database with a primary key of id.
*
* @param id
* the id
* @return the t
*/
public T select(int id) {
JdbcTemplate select = new JdbcTemplate(dataSource);
final Class<T> type = this.type;
final String selectStatement = Dao.selectStatements.get(type);
String selectStatementToUse = null;
// Use the cached value if we have one, otherwise, generate
// one and cache it.
if (selectStatement != null) {
selectStatementToUse = selectStatement;
} else {
selectStatementToUse = strat.createSelectStatement();
Dao.selectStatements.put(type, selectStatementToUse);
}
List<T> results = select.query(selectStatementToUse,
new Object[] { id }, new TemplateRowMapper<T>(type));
if (results.size() > 0) {
return results.get(0);
}
return null;
}
/**
* Selects all T from the database.
*
* @return the list
*/
public List<T> selectAll() {
JdbcTemplate select = new JdbcTemplate(dataSource);
final Class<T> type = this.type;
final String selectAllStatement = Dao.selectAllStatements.get(type);
String selectAllStatementToUse = null;
// Use the cached value if we have one, otherwise, generate
// one and cache it.
if (selectAllStatement != null) {
selectAllStatementToUse = selectAllStatement;
} else {
selectAllStatementToUse = strat.createSelectAllStatement();
Dao.selectAllStatements.put(type, selectAllStatementToUse);
}
List<T> results = select.query(selectAllStatementToUse,
new Object[] {}, new TemplateRowMapper<T>(type));
return results;
}
/**
* Deletes all T from the database.
*/
public void deleteAll() {
JdbcTemplate delete = new JdbcTemplate(dataSource);
final Class<T> type = this.type;
final String deleteAllStatement = Dao.deleteAllStatements.get(type);
String deleteAllStatementToUse = null;
// Use the cached value if we have one, otherwise, generate
// one and cache it.
if (deleteAllStatement != null) {
deleteAllStatementToUse = deleteAllStatement;
} else {
deleteAllStatementToUse = strat.createDeleteAllStatement();
Dao.deleteAllStatements.put(type, deleteAllStatementToUse);
}
delete.update(deleteAllStatementToUse);
}
/**
* Deletes an instance of T from the database based on the Key.
*
* @param t
* the t
*/
public void delete(T t) {
JdbcTemplate delete = new JdbcTemplate(dataSource);
final Class<T> type = this.type;
final String deleteStatement = Dao.deleteStatements.get(type);
String deleteStatementToUse = null;
// Use the cached value if we have one, otherwise, generate
// one and cache it.
if (deleteStatement != null) {
deleteStatementToUse = deleteStatement;
} else {
deleteStatementToUse = strat.createDeleteStatement(t);
Dao.deleteStatements.put(type, deleteStatementToUse);
}
delete.update(deleteStatementToUse,
new Object[] { strat.getPrimaryKeyValue(t) });
}
/**
* Update.
*
* @param t
* the t
* @return the t
*/
public T update(T t) {
JdbcTemplate update = new JdbcTemplate(dataSource);
final Class<T> type = this.type;
final String updateStatement = Dao.updateStatements.get(type);
String updateStatementToUse = null;
// Use the cached value if we have one, otherwise, generate
// one and cache it.
if (updateStatement != null) {
updateStatementToUse = updateStatement;
} else {
updateStatementToUse = strat.createUpdateStatement();
Dao.updateStatements.put(type, updateStatementToUse);
}
update.update(updateStatementToUse, strat.createUpdateObject(t));
T tNew = select((Integer) strat.getPrimaryKeyValue(t));
return tNew;
}
}