package org.jumpmind.db.persist;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.jumpmind.db.model.Column;
import org.jumpmind.db.model.Table;
import org.jumpmind.db.platform.IDatabasePlatform;
import org.jumpmind.db.sql.DmlStatement;
import org.jumpmind.db.sql.DmlStatement.DmlType;
import org.jumpmind.db.sql.Row;
import org.jumpmind.db.sql.SqlException;
import org.jumpmind.persist.AbstractPersistenceManager;
public class JdbcPersistenceManager extends AbstractPersistenceManager {
IDatabasePlatform databasePlatform;
public JdbcPersistenceManager(IDatabasePlatform databasePlatform) {
this.databasePlatform = databasePlatform;
}
/**
* @return true if the object was created, false if the object was updated
*/
@Override
public boolean save(Object object) {
return save(object, null, null, camelCaseToUnderScores(object.getClass().getSimpleName()));
}
/**
* @return true if the object was created, false if the object was updated
*/
@Override
public boolean save(Object object, String catalogName, String schemaName, String tableName) {
if (update(object, catalogName, schemaName, tableName) == 0) {
insert(object, catalogName, schemaName, tableName);
return true;
} else {
return false;
}
}
@Override
public int update(Object object, String catalogName, String schemaName, String tableName) {
return excecuteDml(DmlType.UPDATE, object, catalogName, schemaName, tableName);
}
@Override
public void insert(Object object, String catalogName, String schemaName, String tableName) {
excecuteDml(DmlType.INSERT, object, catalogName, schemaName, tableName);
}
@Override
public boolean delete(Object object) {
return delete(object, null, null, camelCaseToUnderScores(object.getClass().getSimpleName()));
}
@Override
public boolean delete(Object object, String catalogName, String schemaName, String tableName) {
return excecuteDml(DmlType.DELETE, object, catalogName, schemaName, tableName) > 0;
}
@Override
public <T> List<T> find(Class<T> clazz) {
return find(clazz, null, null, camelCaseToUnderScores(clazz.getSimpleName()));
}
@Override
public <T> List<T> find(Class<T> clazz, Map<String, Object> conditions) {
return find(clazz, conditions, null, null, camelCaseToUnderScores(clazz.getSimpleName()));
}
@Override
public <T> T map(Map<String, Object> row, Class<T> clazz, String catalogName, String schemaName, String tableName) {
try {
T object = clazz.newInstance();
Table table = findTable(catalogName, schemaName, tableName);
LinkedHashMap<String, Column> objectToTableMapping = mapObjectToTable(object, table);
Set<String> propertyNames = objectToTableMapping.keySet();
for (String propertyName : propertyNames) {
Object value = row.get(objectToTableMapping.get(propertyName).getName());
BeanUtils.copyProperty(object, propertyName, value);
}
return object;
} catch (Exception e) {
throw toRuntimeException(e);
}
}
@Override
public <T> List<T> find(Class<T> clazz, Map<String, Object> conditions, String catalogName,
String schemaName, String tableName) {
if (conditions == null || conditions.size() == 0) {
return find(clazz, catalogName, schemaName, tableName);
} else {
try {
Table table = findTable(catalogName, schemaName, tableName);
T object = clazz.newInstance();
LinkedHashMap<String, Column> objectToTableMapping = mapObjectToTable(object, table);
LinkedHashMap<String, Object> objectValuesByColumnName = new LinkedHashMap<String, Object>();
Column[] keys = new Column[conditions.size()];
Set<String> keyPropertyNames = conditions.keySet();
boolean[] nullKeyValues = new boolean[conditions.size()];
int index = 0;
for (String propertyName : keyPropertyNames) {
Column column = objectToTableMapping.get(propertyName);
if (column != null) {
keys[index] = column;
nullKeyValues[index] = conditions.get(propertyName) == null;
objectValuesByColumnName
.put(column.getName(), conditions.get(propertyName));
index++;
} else {
throw new IllegalStateException(
"Could not find a database column that maps to the " + propertyName
+ " property on " + clazz.getName()
+ ". Make sure the property is defined on the class and "
+ "the matching column is defined in the database table");
}
}
Column[] columns = objectToTableMapping.values().toArray(
new Column[objectToTableMapping.size()]);
DmlStatement statement = databasePlatform.createDmlStatement(DmlType.SELECT,
table.getCatalog(), table.getSchema(), table.getName(), keys, columns,
nullKeyValues, null);
String sql = statement.getSql();
Object[] values = statement.getValueArray(objectValuesByColumnName);
int[] types = statement.getTypes();
List<Row> rows = databasePlatform.getSqlTemplate().query(sql, values, types);
List<T> objects = new ArrayList<T>();
for (Row row : rows) {
object = clazz.newInstance();
Set<String> propertyNames = objectToTableMapping.keySet();
for (String propertyName : propertyNames) {
Object value = row.get(objectToTableMapping.get(propertyName).getName());
BeanUtils.copyProperty(object, propertyName, value);
}
objects.add(object);
}
return objects;
} catch (Exception e) {
throw toRuntimeException(e);
}
}
}
@Override
public <T> List<T> find(Class<T> clazz, String catalogName, String schemaName, String tableName) {
try {
Table table = findTable(catalogName, schemaName, tableName);
T object = clazz.newInstance();
LinkedHashMap<String, Column> objectToTableMapping = mapObjectToTable(object, table);
Column[] columns = objectToTableMapping.values().toArray(
new Column[objectToTableMapping.size()]);
DmlStatement statement = databasePlatform.createDmlStatement(DmlType.SELECT_ALL,
table.getCatalog(), table.getSchema(), table.getName(), null, columns, null,
null);
String sql = statement.getSql();
List<Row> rows = databasePlatform.getSqlTemplate().query(sql);
List<T> objects = new ArrayList<T>();
for (Row row : rows) {
object = clazz.newInstance();
Set<String> propertyNames = objectToTableMapping.keySet();
for (String propertyName : propertyNames) {
Object value = row.get(objectToTableMapping.get(propertyName).getName());
BeanUtils.copyProperty(object, propertyName, value);
}
objects.add(object);
}
return objects;
} catch (Exception e) {
throw toRuntimeException(e);
}
}
@Override
public void refresh(Object object, String catalogName, String schemaName, String tableName) {
try {
Table table = findTable(catalogName, schemaName, tableName);
LinkedHashMap<String, Column> objectToTableMapping = mapObjectToTable(object, table);
LinkedHashMap<String, Object> objectValuesByColumnName = getObjectValuesByColumnName(
object, objectToTableMapping);
Column[] columns = objectToTableMapping.values().toArray(
new Column[objectToTableMapping.size()]);
List<Column> keys = new ArrayList<Column>(1);
for (Column column : columns) {
if (column.isPrimaryKey()) {
keys.add(column);
}
}
DmlStatement statement = databasePlatform.createDmlStatement(DmlType.SELECT,
table.getCatalog(), table.getSchema(), table.getName(),
keys.toArray(new Column[keys.size()]), columns, null, null);
String sql = statement.getSql();
Object[] values = statement.getValueArray(objectValuesByColumnName);
Row row = databasePlatform.getSqlTemplate().queryForRow(sql, values);
if (row != null) {
Set<String> propertyNames = objectToTableMapping.keySet();
for (String propertyName : propertyNames) {
Object value = row.get(objectToTableMapping.get(propertyName).getName());
BeanUtils.copyProperty(object, propertyName, value);
}
}
} catch (Exception e) {
throw toRuntimeException(e);
}
}
protected int excecuteDml(DmlType type, Object object, String catalogName, String schemaName,
String tableName) {
Table table = findTable(catalogName, schemaName, tableName);
LinkedHashMap<String, Column> objectToTableMapping = mapObjectToTable(object, table);
LinkedHashMap<String, Object> objectValuesByColumnName = getObjectValuesByColumnName(
object, objectToTableMapping);
Column[] columns = objectToTableMapping.values().toArray(
new Column[objectToTableMapping.size()]);
List<Column> keys = new ArrayList<Column>(1);
for (Column column : columns) {
if (column.isPrimaryKey()) {
keys.add(column);
}
}
boolean[] nullKeyValues = new boolean[keys.size()];
int i = 0;
for (Column column : keys) {
nullKeyValues[i++] = objectValuesByColumnName.get(column.getName()) == null;
}
DmlStatement statement = databasePlatform.createDmlStatement(type, table.getCatalog(),
table.getSchema(), table.getName(), keys.toArray(new Column[keys.size()]), columns,
nullKeyValues, null);
String sql = statement.getSql();
Object[] values = statement.getValueArray(objectValuesByColumnName);
int[] types = statement.getTypes();
return databasePlatform.getSqlTemplate().update(sql, values, types);
}
private Table findTable(String catalogName, String schemaName, String tableName) {
Table table = databasePlatform.getTableFromCache(catalogName, schemaName, tableName, false);
if (table == null) {
throw new SqlException("Could not find table "
+ Table.getFullyQualifiedTableName(catalogName, schemaName, tableName));
} else {
return table;
}
}
protected LinkedHashMap<String, Object> getObjectValuesByColumnName(Object object,
LinkedHashMap<String, Column> objectToTableMapping) {
try {
LinkedHashMap<String, Object> objectValuesByColumnName = new LinkedHashMap<String, Object>();
Set<String> propertyNames = objectToTableMapping.keySet();
for (String propertyName : propertyNames) {
objectValuesByColumnName.put(objectToTableMapping.get(propertyName).getName(),
PropertyUtils.getProperty(object, propertyName));
}
return objectValuesByColumnName;
} catch (IllegalAccessException e) {
throw toRuntimeException(e);
} catch (InvocationTargetException e) {
throw toRuntimeException(e);
} catch (NoSuchMethodException e) {
throw toRuntimeException(e);
}
}
protected LinkedHashMap<String, Column> mapObjectToTable(Object object, Table table) {
LinkedHashMap<String, Column> columnNames = new LinkedHashMap<String, Column>();
PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors(object);
for (int i = 0; i < pds.length; i++) {
String propName = pds[i].getName();
Column column = table.getColumnWithName(camelCaseToUnderScores(propName));
if (column != null) {
columnNames.put(propName, column);
}
}
return columnNames;
}
protected String camelCaseToUnderScores(String camelCaseName) {
StringBuilder underscoredName = new StringBuilder();
for (int p = 0; p < camelCaseName.length(); p++) {
char c = camelCaseName.charAt(p);
if (p > 0 && Character.isUpperCase(c)) {
underscoredName.append("_");
}
underscoredName.append(Character.toLowerCase(c));
}
return underscoredName.toString();
}
public void setDatabasePlatform(IDatabasePlatform databasePlatform) {
this.databasePlatform = databasePlatform;
}
public IDatabasePlatform getDatabasePlatform() {
return databasePlatform;
}
}