package org.rr.jeborker.db;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.rr.commons.collection.ICloseableList;
import org.rr.commons.log.LoggerFactory;
import org.rr.commons.utils.ReflectionUtils;
import org.rr.commons.utils.StringUtil;
import org.rr.jeborker.db.item.EbookPropertyItem;
import org.rr.jeborker.db.item.EbookPropertyItemUtils;
import org.rr.jeborker.db.item.PreferenceItem;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.jdbc.JdbcPooledConnectionSource;
import com.j256.ormlite.stmt.QueryBuilder;
import com.j256.ormlite.stmt.Where;
/**
* The {@link DefaultDBManager} provides methods for handle database connections and it's content.
*
* @param <T>
*/
public abstract class DefaultDBManager {
private static DefaultDBManager manager;
protected static final Class<?>[] KNOWN_CLASSES = new Class<?>[] { EbookPropertyItem.class, PreferenceItem.class };
private JdbcPooledConnectionSource connection;
/**
* Gets a shared {@link ConfigManager} instance.
*
* @return The desired {@link ConfigManager} instance.
*/
public synchronized static DefaultDBManager getInstance() {
if (manager == null) {
manager = new H2DBManager();
JdbcPooledConnectionSource initDatabase = manager.initDatabase();
manager.connection = initDatabase;
}
return manager;
}
protected DefaultDBManager() {
}
/**
* Initializes the database system to be used by this {@link DefaultDBManager} instance.
*/
protected abstract JdbcPooledConnectionSource initDatabase();
/**
* @return the connection pool used by this {@link DefaultDBManager} instance.
*/
protected JdbcPooledConnectionSource getConnectionPool() {
return connection;
}
protected void setConnectionPool(JdbcPooledConnectionSource connection) {
this.connection = connection;
}
/**
* Closes and shutdown all database connections previously opened.
*/
public synchronized void shutdown() {
try {
connection.close();
} catch (SQLException e) {
LoggerFactory.log(Level.SEVERE, this, "shutdown database has failed", e);
}
}
/**
* Get a {@link QueryBuilder} instance for the given entity class.
* @return a new {@link QueryBuilder} instance for the given class. Never returns <code>null</code>
* @throws RuntimeException if an SQL error occurs.
*/
public synchronized <T> QueryBuilder<T, T> getQueryBuilder(Class<T> cls) {
Dao<T, T> createDao;
try {
createDao = DaoManager.createDao(connection, cls);
QueryBuilder<T, T> queryBuilder = createDao.queryBuilder();
return queryBuilder;
} catch (SQLException e) {
throw new RuntimeException("Failed to create dao for " + cls, e);
}
}
public synchronized IDBObject storeObject(final IDBObject item) {
try {
Dao<IDBObject, ?> createDao = (Dao<IDBObject, ?>) DaoManager.createDao(connection, item.getClass());
createDao.createOrUpdate(item);
} catch (Exception e) {
LoggerFactory.log(Level.SEVERE, this, "Failed to store object " + item, e);
}
return item;
}
/**
* Gets simply all items from the database which matches to the given class type.
*
* @param cls The class type of the pojos to be fetched
* @return A Iterable which provides the data. Never returns <code>null</code>.
*/
public synchronized <T> Collection<T> getItems(Class<T> cls) {
try {
Dao<IDBObject, T> createDao = (Dao<IDBObject, T>) DaoManager.createDao(connection, cls);
List<T> queryForAll = (List<T>) createDao.queryForAll();
return queryForAll;
} catch (Exception e) {
LoggerFactory.log(Level.SEVERE, this, "Failed to query objects " + cls, e);
return Collections.emptyList();
}
}
/**
* Gets simply all items from the database which matches to the given class type.
*
* @param cls The class type of the pojos to be fetched
* @return A Iterable which provides the data. Never returns <code>null</code>.
*/
public synchronized <T> List<T> getItems(final Class<T> cls, Where<T, T> where, final List<Field> orderFields,
final OrderDirection orderDirection) {
try {
Dao<T, T> createDao = DaoManager.createDao(connection, cls);
QueryBuilder<T, T> queryBuilder = createDao.queryBuilder();
if(where != null && !DBUtils.isEmpty(where)) {
queryBuilder.setWhere(where);
}
for(Field orderField : orderFields) {
queryBuilder.orderBy(orderField.getName(), orderDirection.isAscending());
}
//System.out.println(DBUtils.isEmpty(where) ? "" : where.getStatement());
List<T> queryRaw = createDao.query(queryBuilder.prepare());
//System.out.println(queryRaw.size() + " items fetched");
return queryRaw;
} catch (Exception e) {
LoggerFactory.logWarning(this, "Reading database entries has failed", e);
return Collections.emptyList();
}
}
public abstract <T> ICloseableList<T> queryFullTextSearch(Class<T> cls, Where<T, T> where, List<String> keywords, List<Field> orderFields,
OrderDirection orderDirection);
/**
* Updates the given item.
*
* @param item
* {@link IDBObject} instance to be updated.
*/
public synchronized IDBObject updateObject(final IDBObject item) {
// store the bytes before deleting
final HashMap<Field, byte[]> data = new HashMap<Field, byte[]>();
// restore binary data to IDBObject
for (Map.Entry<Field, byte[]> entry : data.entrySet()) {
Field field = entry.getKey();
byte[] value = entry.getValue();
try {
field.set(item, value);
} catch (Exception e) {
LoggerFactory.getLogger(this).log(Level.WARNING, "could not restore binary data for " + item, e);
}
}
// store the item and
return this.storeObject(item);
}
/**
* Simple getObject method which searches for an entry with the given class type allowing to specify one field with a value as condition.
*
* @param <T>
* The class type to be searched
* @param class1
* The entry class type.
* @param field
* The name of the field for the condition
* @param value
* The condition value.
* @return A list with all results.
*/
public synchronized <T> List<T> getObject(Class<T> class1, final String field, final String value) {
try {
Dao<T, T> createDao = DaoManager.createDao(connection, class1);
QueryBuilder<T, T> queryBuilder = createDao.queryBuilder();
queryBuilder.where().eq(field, StringUtil.escapeSql(value));
List<T> query = createDao.query(queryBuilder.prepare());
return query;
} catch(Exception e) {
LoggerFactory.getLogger(this).log(Level.WARNING, "getObject for " + class1 + " and field=" + field + " and value=" + value + " failed.", e);
return Collections.emptyList();
}
}
public synchronized boolean deleteObject(IDBObject item) {
return deleteObject(item, true);
}
/**
* Deletes the given item and it's binary entries from the database.
*
* @param item
* The item to be deleted.
* @return
*/
public synchronized boolean deleteObject(IDBObject item, boolean deleteCover) {
try {
Dao<IDBObject, ?> createDao = (Dao<IDBObject, ?>) DaoManager.createDao(connection, item.getClass());
createDao.delete(item);
if (item instanceof EbookPropertyItem) {
EbookPropertyItemUtils.deleteCoverThumbnail(((EbookPropertyItem) item).getResourceHandler());
}
} catch (Exception e) {
LoggerFactory.getLogger(this).log(Level.WARNING, "failed to delete " + item, e);
return false;
}
return true;
}
/**
* Gets an instance for the desired {@link IDBObject} class.
*
* @param item
* The class for the new {@link IDBObject}.
* @return The desired object instance.
*/
public <T> T newInstance(Class<T> item) {
return (T) ReflectionUtils.getObjectInstance(item, null);
}
/**
* Rereads the given item from the database.
*
* @return The new item or <code>null</code> if the item is no longer present in the database.
*/
public IDBObject reload(IDBObject item) {
if (item != null) {
try {
Dao<IDBObject, ?> createDao = (Dao<IDBObject, ?>) DaoManager.createDao(connection, item.getClass());
createDao.refresh(item);
} catch(Exception e) {
LoggerFactory.getLogger(this).log(Level.WARNING, "failed to load " + item, e);
}
}
return item;
}
}