Skip to content

Instantly share code, notes, and snippets.

@gurpreet-
Created March 8, 2018 00:47
Show Gist options
  • Save gurpreet-/8062ba4fff9e43797ecc40bc4852e74d to your computer and use it in GitHub Desktop.
Save gurpreet-/8062ba4fff9e43797ecc40bc4852e74d to your computer and use it in GitHub Desktop.
Making Apache Cayenne respect default values in Postgres
/**
* Modified from http://objectstyle.org/cayenne/lists/cayenne-user/2007/12/0084.html
* for use with Postgres. However, it performs many SELECTs which may or may not be
* efficient depending on your application.
*/
private void setNewObjectDefaults() {
BaseDataObject mainObj = (BaseDataObject) this;
ObjEntity ent = Cayenne.getObjEntity(mainObj);
Collection attribs = ent.getAttributes();
// Try and get the default values from the database
String defaultSql = getDefaultValueLookupQuery(ent.getDbEntityName());
SQLTemplate rawSelect = new SQLTemplate(mainObj.getClass(), defaultSql);
rawSelect.setFetchingDataRows(true);
// Now get a list of column names and results.
List defList = mainObj.getObjectContext().performQuery(rawSelect);
// Loop over the attributes that the mainObj has.
for (int i = 0; i <= attribs.size() - 1; i++) {
ObjAttribute attr = (ObjAttribute) attribs.toArray()[i];
// We need to find something out about this attribute
DbAttribute att = attr.getDbAttribute();
String propName = attr.getName();
Object propertyValue = mainObj.readProperty(propName);
Class propertyType = attr.getJavaClass();
Object def = null;
// Do not process if there is already a value
// in the object's attributes
if (propertyValue != null) {
continue;
}
// Now that we're looping over the attributes,
// we should also loop over the default values returned above
// and check if they are both in each others' list
for (int j = 0; j <= defList.size() - 1; j++) {
Map row = (Map) defList.get(j);
String rowFieldName = (String) row.get("column_name");
// If they are in each others' list then set the column default
// as the default object
if (rowFieldName.equals(att.getName())) {
def = row.get("column_default");
break;
}
}
Object result = getDefaultValue(def, mainObj.getObjectContext());
// If the value is null then write null
if (result == null) {
mainObj.writeProperty(propName, null);
continue;
}
// Here we check what class the result is.
// The result may be a Date or a String or anything defined
// by SQLDataTypes, so we have to check what type it is below.
Class<?> keyClass = result.getClass();
// TODO: This may need modifying depending on what you get returned
if (keyClass.equals(Date.class)) {
Date d = (Date) result;
String ds = d.toString();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss zzz yyyy");
LocalDateTime l = LocalDateTime.parse(ds, dtf);
mainObj.writeProperty(propName, l);
} else if (keyClass.equals(Long.class)) {
mainObj.writeProperty(propName, result);
} else if (keyClass.equals(Integer.class)) {
mainObj.writeProperty(propName, result);
} else {
// Just try writing - warning this may error
// because I haven't written in all the classes
// that the default value may return
mainObj.writeProperty(propName, result);
}
}
}
/**
* Because the defaultValue may be an expression, here what we're doing
* is SELECTing that expression to return it.
* @param defaultValue The default value returned from a query, should be a String
* @param context An ObjectContext to perform finding
* @return The actual default value
*/
public Object getDefaultValue(Object defaultValue, ObjectContext context) {
SQLTemplate sel = new SQLTemplate("SELECT " + defaultValue, true);
List query = context.performQuery(sel);
// This returns a one column table with for example "now"
// or "int8" as the heading and the value.
Map table = (Map) query.get(0);
// Grab that one column's entry
java.util.HashMap.Entry firstCol = (java.util.HashMap.Entry) table.entrySet().toArray()[0];
// .. and then return the value
return firstCol.getValue();
}
protected String getDefaultValueLookupQuery(String entName) {
String def = "SELECT column_name, column_default " +
"FROM information_schema.columns " +
"WHERE (table_schema, table_name) = ('public', '" + entName + "') " +
"ORDER BY ordinal_position;";
return def;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment