package com.androideasyapps.phoenix.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import com.androideasyapps.phoenix.dao.AbstractPersistenceManager.ConnectionWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // TODO: we might need to create an IModel interface for all model objects // TODO: create a central PersistenceManager that could be a singleton that is the // central point for all DAO objects. public abstract class BaseDAO<PM extends AbstractPersistenceManager, T extends ChangesBean> { protected static final Logger log = LoggerFactory.getLogger("com.androideasyapps.phoenix.dao.db"); protected PM persistenceManager; public BaseDAO(PM pm) { this.persistenceManager = pm; } public abstract String getTableName(); public abstract String[] getColumnNames(); protected abstract String[] getCREATESQL(); protected abstract PreparedStatement prepareInsert(Connection conn, T item) throws SQLException; protected abstract T newItem(); protected abstract T copyResultSetToItem(ResultSet rs, T item, Connection conn) throws Exception; protected abstract void updateForeignCollections(T item, Connection conn) throws Exception; // public abstract int createIndexes(); // public abstract int upgrade(); // see upgrade note in NOTES.txt // public abstract int update(T item); // // // partial object // public abstract Collection<T> query(String[] columns, String where, // Object... args); // // full query public Collection<T> query(String where, Object... args) throws Exception { ConnectionWrapper c = persistenceManager.getTransactionalConnection(); try { Connection conn = c.getConnection(); StringBuilder sql = new StringBuilder("select * from " + getTableName()); if (where != null) sql.append(" where ").append(where); PreparedStatement p = conn.prepareStatement(sql.toString()); if (args != null) { for (int i = 0; i < args.length; i++) { p.setObject(i + 1, args[i]); } } ResultSet rs = p.executeQuery(); List<T> rows = new ArrayList<T>(); while (rs.next()) { rows.add(copyResultSetToItem(rs, newItem(), conn)); } return rows; } finally { release(c); } } public Collection<T> queryAll() throws Exception { return query(null); } private void release(ConnectionWrapper c) { // work around so that we can use it in android, which doesn't support // try with resources :( if (c!=null) { try { c.close(); } catch (Exception e) { e.printStackTrace(); } } } public Collection<T> rawQuery(String sql, Object... args) throws Exception { ConnectionWrapper c = persistenceManager.getTransactionalConnection(); try { return rawQuery(c.getConnection(), sql, args); } finally { release(c); } } /** * Executes the query and return the first field, in the first result. (useful for things * like, select max(field) from table * @param sql * @param args * @return value as object, or null. * @throws Exception */ public <T2> T2 rawQueryFirstField(String sql, Object... args) throws Exception { ConnectionWrapper c = persistenceManager.getTransactionalConnection(); try { PreparedStatement p = c.getConnection().prepareStatement(sql.toString()); if (args != null) { for (int i = 0; i < args.length; i++) { p.setObject(i + 1, args[i]); } } ResultSet rs = p.executeQuery(); if (rs.next()) { return (T2) rs.getObject(1); } } finally { release(c); } return null; } public long max(String field) throws Exception { Number val = rawQueryFirstField("select max("+field+") from " + getTableName(), null); if (val==null) return 0; return val.longValue(); } public Collection<T> rawQuery(Connection conn, String sql, Object... args) throws Exception { PreparedStatement p = conn.prepareStatement(sql.toString()); if (args != null) { for (int i = 0; i < args.length; i++) { p.setObject(i + 1, args[i]); } } ResultSet rs = p.executeQuery(); List<T> rows = new ArrayList<T>(); while (rs.next()) { rows.add(copyResultSetToItem(rs, newItem(), conn)); } return rows; } public void save(T item) throws Exception { if (((HasID) item).getId() == 0) { insert(item); } else { updateChanges(item); } } public void save(T item, Connection conn) throws Exception { if (((HasID) item).getId() == 0) { insert(item, conn); } else { updateChanges(item, conn); } } public T refresh(T item) throws Exception { return findById(((HasID) item).getId(), item); } public void create() throws Exception { ConnectionWrapper c = persistenceManager.getAutoComitConnection(); try { Connection conn = c.getConnection(); for (String sql : getCREATESQL()) { conn.createStatement().execute(sql); } doSchemaUpdates(conn); } finally { release(c);; } } protected abstract void doSchemaUpdates(Connection conn) throws Exception; public int insert(T item) throws Exception { ConnectionWrapper c = persistenceManager .getTransactionalConnection(); try { return insert(item, c.getConnection()); } finally { release(c); } } public int insert(T item, Connection conn) throws Exception { // TODO: we need to insert foriegn objects first, and get it's result // value // and set in the foriegn reference of this object, and then save this // object PreparedStatement ps = prepareInsert(conn, item); try { int i = ps.executeUpdate(); if (i > 0) { setId(item, ps); } updateForeignCollections(item, conn); // once we save, we need to reset the changed status item.setChanged(false); return i; } finally { ps.close(); } } protected void updatexxxCastMemberForeignCollection(T item, Connection conn) { // ChangesCollection<CastMember> coll = item.getCastMembers(); // if (!coll.isChanged()) return; // do nothing, since nothing has // changed. // process additions // CastMemberH2DAO relatedDao = persistenceManager.getCastMemberDAO(); // for (CastMember cb: coll.additions) { // // save the item, if necessary // if (cb.isChanged()) relatedDao.save(cb, conn); // // // insert the relationship // try { // PreparedStatement ps = // conn.prepareStatement("insert into MEDIAFILE_CASTMEMBER (MEDIFILE_ID, CASTMEMBER_ID) values (?, ?)"); // ps.setLong(1, item.getId()); // ps.setLong(2, cb.getId()); // ps.executeUpdate(); // } catch (Exception e) { // // row exists... // } // } // // process updates // for (CastMember cb: coll.additions) { // // save the item, if necessary // if (cb.isChanged()) relatedDao.save(conn, cb); // // // insert the relationship (is this necessary) // try { // PreparedStatement ps = // conn.prepareStatement("insert into MEDIAFILE_CASTMEMBER (MEDIFILE_ID, CASTMEMBER_ID) values (?, ?)"); // ps.setLong(1, item.getId()); // ps.setLong(2, cb.getId()); // } catch (Exception e) { // // row exists... // } // } // // process deletes // for (CastMember cb: coll.additions) { // // save the item, if necessary // if (cb.isChanged()) relatedDao.save(conn, cb); // // // delete the relationship... we do not delete the item, though // PreparedStatement ps = // conn.prepareStatement("delete from MEDIAFILE_CASTMEMBER where MEDIFILE_ID=? and CASTMEMBER_ID=?"); // ps.setLong(1, item.getId()); // ps.setLong(2, cb.getId()); // } } public int updateChanges(T item) throws Exception { if (item == null) return 0; if (item.getChanges().size() == 0) { log.debug("Nothing Changed for item {}; Class: ", ((HasID) item).getId(), ((Object)item).getClass().getName()); return 0; } ConnectionWrapper c = persistenceManager .getTransactionalConnection(); try { return updateChanges(item, c.getConnection()); } finally { release(c); } } public int updateChanges(T item, Connection conn) throws Exception { updateForeignCollections(item, conn); StringBuilder cols = new StringBuilder(); for (Map.Entry<String, Object> me : item.getChanges().entrySet()) { if (cols.length() > 0) { cols.append(","); } cols.append(me.getKey()).append("=?"); } int i = 1; PreparedStatement ps = conn.prepareStatement("update " + getTableName() + " set " + cols.toString() + " where _id=?"); for (Map.Entry<String, Object> me : item.getChanges().entrySet()) { ps.setObject(i++, me.getValue()); } ps.setLong(i, ((HasID) item).getId()); int v = ps.executeUpdate(); if (v > 0) { item.setChanged(false); } ps.close(); return v; } public T setId(T in, Statement st) throws SQLException { // http://stackoverflow.com/questions/1915166/how-to-get-the-insert-id-in-jdbc // we inserted a record, get the id ResultSet generatedKeys = st.getGeneratedKeys(); try { if (generatedKeys.next()) { ((HasID) in).setId(generatedKeys.getLong(1)); } else { throw new SQLException("Creating user failed, no ID obtained."); } } finally { generatedKeys.close(); } return in; } public int delete(T item) throws Exception { return delete(((HasID) item).getId()); } public int delete(long id) throws Exception { ConnectionWrapper c = persistenceManager .getTransactionalConnection(); try { Connection conn = c.getConnection(); PreparedStatement p = conn.prepareStatement("delete from " + getTableName() + " where _id = ?"); p.setLong(1, id); return p.executeUpdate(); } finally { release(c); } } public int deleteAll() throws Exception { ConnectionWrapper c = persistenceManager .getTransactionalConnection(); try { Connection conn = c.getConnection(); PreparedStatement p = conn.prepareStatement("delete from " + getTableName()); return p.executeUpdate(); } finally { release(c); } } public int deleteWhere(String where, Object...args) throws Exception { ConnectionWrapper c = persistenceManager.getTransactionalConnection(); try { Connection conn = c.getConnection(); StringBuilder sql = new StringBuilder("delete from " + getTableName()); if (where != null) sql.append(" where ").append(where); PreparedStatement p = conn.prepareStatement(sql.toString()); if (args != null) { for (int i = 0; i < args.length; i++) { p.setObject(i + 1, args[i]); } } return p.executeUpdate(); } finally { release(c); } } public long count() throws Exception { ConnectionWrapper c = persistenceManager .getTransactionalConnection(); try { Connection conn = c.getConnection(); PreparedStatement p = conn.prepareStatement("select count(1) from " + getTableName()); ResultSet rs = p.executeQuery(); if (rs.first()) { return rs.getLong(1); } return 0; } finally { release(c); } } public T findById(long id) throws Exception { return findById(id, newItem()); } protected T findById(long id, T item) throws Exception { ConnectionWrapper c = persistenceManager .getTransactionalConnection(); try { Connection conn = c.getConnection(); PreparedStatement p = conn.prepareStatement("select * from " + getTableName() + " where _id = ?"); p.setLong(1, id); ResultSet rs = p.executeQuery(); if (rs.first()) { copyResultSetToItem(rs, item, conn); return item; } return null; } finally { release(c); } } public T queryFirst(String where, Object... args) throws Exception { ConnectionWrapper c = persistenceManager .getTransactionalConnection(); try { Connection conn = c.getConnection(); PreparedStatement p = conn.prepareStatement("select * from " + getTableName() + " where " + where); if (args != null && args.length > 0) { for (int i = 0; i < args.length; i++) { p.setObject(i + 1, args[i]); } } ResultSet rs = p.executeQuery(); if (rs.first()) { T item = newItem(); copyResultSetToItem(rs, item, conn); return item; } return null; } finally { release(c); } } }