JavaXT
|
|
Model Classpackage javaxt.orm; import javaxt.json.*; import java.util.*; import java.util.stream.Collectors; //****************************************************************************** //** Model Class //****************************************************************************** /** * Used to parse a model and generate code (e.g. Java and DDL). * ******************************************************************************/ public class Model { private String name; private TreeSet<String> implementations; private ArrayList<Field> fields; private static final String template = getTemplate(); private String tableName; private String escapedTableName; private String packageName; private String schemaName; private String escapedSchemaName; private HashMap<String, String> options; //************************************************************************** //** Constructor //************************************************************************** /** Creates a new instance of this class. */ protected Model(String modelName, JSONObject modelInfo, String packageName, HashMap<String, String> options){ this.name = modelName; this.implementations = new TreeSet<>(); this.fields = new ArrayList<>(); this.options = options; this.packageName = packageName; this.tableName = Utils.camelCaseToUnderScore(name).toLowerCase(); this.schemaName = options.get("schema"); if (schemaName==null){ escapedTableName = escapeTableName(tableName); } else{ escapedTableName = tableName.toUpperCase(); escapedSchemaName = escapeTableName(schemaName); escapedTableName = escapedSchemaName + "." + escapedTableName; } //Get implementation classes JSONArray implementations = modelInfo.get("implements").toJSONArray(); if (implementations!=null){ for (JSONValue i : implementations){ this.implementations.add(i.toString()); } } //Parse fields JSONArray arr = modelInfo.get("fields").toJSONArray(); if (arr!=null){ for (JSONValue f : arr){ String name = f.get("name").toString(); String type = f.get("type").toString(); //Don't add ID field. It is added automatically. if (name.equalsIgnoreCase("id")) continue; //Create field and update the fields array Field field = new Field(name, type); addConstraints(field, f.toJSONObject()); this.fields.add(field); } } //Parse hasMany JSONArray hasMany = modelInfo.get("hasMany").toJSONArray(); if (hasMany!=null) for (int i=0; i<hasMany.length(); i++){ JSONObject json = hasMany.get(i).toJSONObject(); Field field = new Field(json.get("name").toString(), json.get("model").toString()+"[]"); fields.add(field); } //Parse constraints JSONArray constraints = modelInfo.get("constraints").toJSONArray(); if (constraints!=null){ for (JSONValue constraint : constraints){ String fieldName = constraint.get("name").toString(); for (Field field : fields){ if (field.getName().equals(fieldName)){ addConstraints(field, constraint.toJSONObject()); break; } } } } //Parse default values JSONArray defaultValues = modelInfo.get("defaults").toJSONArray(); if (defaultValues!=null){ for (JSONValue d : defaultValues){ String fieldName = d.get("name").toString(); for (Field field : fields){ if (field.getName().equals(fieldName)){ field.setDefaultValue(d.get("value").toObject()); break; } } } } } //************************************************************************** //** addConstraints //************************************************************************** /** Used to add constraints to a given field. Constraints can be defined in * a "constraints" array or inline with the field definition. If both are * present, the constraints in the "constraints" array may override * constraints defined inline. */ private void addConstraints(Field field, JSONObject constraint){ Boolean isRequired = constraint.get("required").toBoolean(); if (isRequired==null) isRequired = constraint.get("nullable").toBoolean(); Boolean isUnique = constraint.get("unique").toBoolean(); Integer length = constraint.get("length").toInteger(); if (length==null) length = constraint.get("size").toInteger(); Integer srid = constraint.get("srid").toInteger(); if (isRequired!=null) field.isRequired(isRequired); if (isUnique!=null) field.isUnique(isUnique); if (length!=null) field.setLength(length); if (srid!=null) field.setSRID(srid); ForeignKey foreignKey = field.getForeignKey(); if (foreignKey!=null){ String onDelete = constraint.get("onDelete").toString(); //Special case for models. Cascade delete by default if (onDelete==null && field.isModel()){ onDelete = "cascade"; } if (onDelete!=null){ foreignKey.onDelete(onDelete); } } if (constraint.has("default")){ Object defaultValue = constraint.get("default").toObject(); field.setDefaultValue(defaultValue); } } //************************************************************************** //** getName //************************************************************************** /** Returns the name of this model. */ public String getName(){ return name; } //************************************************************************** //** getPackageName //************************************************************************** public String getPackageName(){ return packageName; } //************************************************************************** //** getFields //************************************************************************** /** Returns an array of all the fields found in this model. */ public Field[] getFields(){ return fields.toArray(new Field[fields.size()]); } //************************************************************************** //** getTableName //************************************************************************** /** Returns the name of the table associated with this model. */ public String getTableName(){ return tableName; } //************************************************************************** //** getSchemaName //************************************************************************** public String getSchemaName(){ return schemaName; } //************************************************************************** //** getJavaCode //************************************************************************** /** Used to generate Java code for the model. */ public String getJavaCode(){ String str = template.replace("${modelName}", name); str = str.replace("${package}", packageName); str = str.replace("${tableName}", schemaName==null ? tableName : (schemaName + "." + tableName)); str = str.replace("${implements}", implementations.isEmpty() ? "" : "\r\n implements " + implementations.stream().map(Object::toString).collect(Collectors.joining(", ")) + " "); StringBuilder fieldMap = new StringBuilder("\r\n"); StringBuilder privateFields = new StringBuilder(); StringBuilder publicMembers = new StringBuilder(); StringBuilder getModels = new StringBuilder(); StringBuilder saveModels = new StringBuilder(); StringBuilder getValues = new StringBuilder(); StringBuilder getJson = new StringBuilder(); StringBuilder toJson = new StringBuilder(); StringBuilder hasMany = new StringBuilder(); StringBuilder initArrays = new StringBuilder(); String getLastModified = ""; TreeSet<String> includes = new TreeSet<>(); //Special case for models that implement java.security.Principal boolean hasNameField = false; String getSecurityPrincipalName = ""; if (implementations.contains("java.security.Principal")){ //Check if fields have a name field for (Field field : fields){ if (field.getName().equalsIgnoreCase("name")){ hasNameField = true; break; } } //If no name field is found, we'll need to add a public getName() if (!hasNameField){ getSecurityPrincipalName += " public String getName(){\r\n"; getSecurityPrincipalName += " return null;\r\n"; getSecurityPrincipalName += " }\r\n\r\n"; } } //Loop through all the fields and generate java code for (int i=0; i<fields.size(); i++){ Field field = fields.get(i); String fieldName = Utils.underscoreToCamelCase(field.getName()); String fieldType = field.getType(); String methodName = Utils.capitalize(fieldName); String columnName = field.getColumnName(); String modelName = null; if (field.isArray()){ modelName = fieldType.substring(10, fieldType.length()-1); includes.add("java.util.ArrayList"); } boolean password = fieldType.equals("Password"); if (password) includes.add("javaxt.encryption.BCrypt"); if (fieldType.equals("Date")) includes.add("javaxt.utils.Date"); if (fieldType.equals("BigDecimal")) includes.add("java.math.BigDecimal"); if (fieldType.equals("Geometry")){ String jts = options.get("jts"); if (jts==null) jts = "org.locationtech.jts"; //vs "com.vividsolutions.jts"; includes.add(jts+".geom.Geometry"); includes.add(jts+".io.WKTReader"); } /* For Java 8 and below //Append field to the fieldMap fieldMap.append(" put(\""); fieldMap.append(fieldName); fieldMap.append("\", \""); fieldMap.append(columnName); fieldMap.append("\");\r\n"); */ //Append field to the fieldMap fieldMap.append(" java.util.Map.entry(\""); fieldMap.append(fieldName); fieldMap.append("\", \""); fieldMap.append(columnName); fieldMap.append("\")"); if (i<fields.size()-1) fieldMap.append(","); fieldMap.append("\r\n"); //Append private field if (!password){ privateFields.append(" private "); privateFields.append(fieldType); privateFields.append(" "); privateFields.append(fieldName); privateFields.append(";\r\n"); } else{ privateFields.append(" private String "); privateFields.append(fieldName); privateFields.append("; //bcrypt hash\r\n"); } //Append public get method if (!field.isArray()){ if (password){ publicMembers.append(" /** Returns a BCrypt encrypted password */\r\n"); } publicMembers.append(" public "); if (password){ publicMembers.append("String"); } else{ publicMembers.append(fieldType); } publicMembers.append(" get"); publicMembers.append(methodName); publicMembers.append("(){\r\n"); publicMembers.append(" return "); publicMembers.append(fieldName); publicMembers.append(";\r\n"); publicMembers.append(" }\r\n\r\n"); //Add extra method if password if (password){ //Add an authenticate method for password fields publicMembers.append(" public boolean authenticate(String "); publicMembers.append(fieldName); publicMembers.append("){\r\n"); publicMembers.append(" return BCrypt.checkpw("); publicMembers.append(fieldName); publicMembers.append(", this."); publicMembers.append(fieldName); publicMembers.append(");\r\n"); publicMembers.append(" }\r\n\r\n"); } //Special case for models that implement java.security.Principal //If there's no name field, use the username as a return value //for the getName() method if (fieldName.equals("username") && fieldType.equals("String") && implementations.contains("java.security.Principal") && !hasNameField){ getSecurityPrincipalName = getSecurityPrincipalName.replace("null", fieldName); } } else{ publicMembers.append(" public "); publicMembers.append(modelName); publicMembers.append("[] get"); publicMembers.append(methodName); publicMembers.append("(){\r\n"); publicMembers.append(" return "); publicMembers.append(fieldName); publicMembers.append(".toArray(new "); publicMembers.append(modelName); publicMembers.append("["); publicMembers.append(fieldName); publicMembers.append(".size()]);\r\n"); publicMembers.append(" }\r\n\r\n"); } //Append public set method if (!field.isLastModifiedDate()){ if (!field.isArray()){ publicMembers.append(" public void set"); publicMembers.append(methodName); publicMembers.append("("); publicMembers.append(password ? "String" : fieldType); publicMembers.append(" "); publicMembers.append(fieldName); publicMembers.append("){\r\n"); if (password){ publicMembers.append(" if (BCrypt.hasSalt("); publicMembers.append(fieldName); publicMembers.append(")) this."); publicMembers.append(fieldName); publicMembers.append(" = "); publicMembers.append(fieldName); publicMembers.append(";\r\n"); publicMembers.append(" else this."); } else{ publicMembers.append(" this."); } publicMembers.append(fieldName); publicMembers.append(" = "); if (password){ publicMembers.append("BCrypt.hashpw(" + fieldName + ", BCrypt.gensalt())"); } else{ publicMembers.append(fieldName); } publicMembers.append(";\r\n"); publicMembers.append(" }\r\n\r\n"); } else{ publicMembers.append(" public void set"); publicMembers.append(methodName); publicMembers.append("("); publicMembers.append(modelName); publicMembers.append("[] arr){\r\n"); publicMembers.append(" " + fieldName + " = new " + fieldType + "();\r\n"); publicMembers.append(" for (int i=0; i<arr.length; i++){\r\n"); publicMembers.append(" "); publicMembers.append(fieldName); publicMembers.append(".add(arr[i]);\r\n"); publicMembers.append(" }\r\n"); publicMembers.append(" }\r\n\r\n"); publicMembers.append(" public void add"); publicMembers.append(modelName); publicMembers.append("("); String paramName = modelName.substring(0, 1).toLowerCase() + modelName.substring(1); publicMembers.append(modelName + " " + paramName); publicMembers.append("){\r\n"); publicMembers.append(" this." + fieldName + ".add(" + paramName + ");\r\n"); publicMembers.append(" }\r\n\r\n"); } } //Update database constructor if (!field.isArray()){ if (!field.isModel()){ if (fieldType.equals("JSONObject")){ getValues.append(" this."); getValues.append(fieldName); getValues.append(" = new JSONObject(getValue(rs, \""); getValues.append(columnName); getValues.append("\").toString());\r\n"); } else if (fieldType.equals("byte[]")){ getValues.append(" this."); getValues.append(fieldName); getValues.append(" = getValue(rs, \""); getValues.append(columnName); getValues.append("\").toByteArray();\r\n"); } else if (fieldType.equals("Geometry")){ getValues.append(" try{this."); getValues.append(fieldName); getValues.append(" = new WKTReader().read(getValue(rs, \""); getValues.append(columnName); getValues.append("\").toString());}catch(Exception e){}\r\n"); } else{ if (fieldType.endsWith("[]")){ //e.g. String[] getValues.append(" {"); //Object[] v = (Object[])getValue(rs, "recent_customers").toArray(); getValues.append("Object[] v = (Object[]) getValue(rs, \""); getValues.append(columnName); getValues.append("\").toArray();\r\n"); //this.recentCustomers = java.util.Arrays.copyOf(v, v.length, String[].class); getValues.append(" this."); getValues.append(fieldName); getValues.append(" = v==null ? null : java.util.Arrays.copyOf(v, v.length, "); getValues.append(fieldType); getValues.append(".class);"); getValues.append("}\r\n"); } else{ getValues.append(" this."); getValues.append(fieldName); getValues.append(" = getValue(rs, \""); getValues.append(columnName); getValues.append("\").to"); getValues.append(password ? "String" : fieldType); getValues.append("();\r\n"); } } } else{ String id = Utils.underscoreToCamelCase(fieldName) + "ID"; getValues.append(" Long "); getValues.append(id); getValues.append(" = getValue(rs, \""); getValues.append(columnName); getValues.append("\").toLong();\r\n"); getModels.append("\r\n\r\n"); getModels.append(" //Set " + fieldName + "\r\n"); getModels.append(" if (" + id + "!=null) "); getModels.append(fieldName + " = new " + fieldType + "(" + id + ");\r\n"); } } else{ String leftTable = this.tableName; String leftColumn = leftTable + "_id"; String rightTable = Utils.camelCaseToUnderScore(modelName).toLowerCase(); String rightColumn = rightTable + "_id"; //Special case for when a model hasMany of itself if (leftTable.equals(rightTable)) rightColumn = rightTable + "_id2"; String tableName = leftTable + "_" + rightTable; if (schemaName!=null) tableName = escapedSchemaName + "." + tableName; tableName = tableName.toLowerCase(); String idArray = modelName + "IDs"; idArray = idArray.substring(0, 1).toLowerCase() + idArray.substring(1); String id = modelName + "ID"; id = id.substring(0, 1).toLowerCase() + id.substring(1); //Update get models (see database constructor) hasMany.append("\r\n\r\n"); hasMany.append(" //Set " + fieldName + "\r\n"); hasMany.append(" for (javaxt.sql.Record record : conn.getRecords(\r\n"); hasMany.append(" \"select " + rightColumn + " from " + tableName + " where " + leftColumn + "=\"+id)){\r\n"); hasMany.append(" " + fieldName + ".add(new " + modelName + "(record.get(0).toLong()));\r\n"); hasMany.append(" }\r\n"); initArrays.append(" " + fieldName + " = new " + fieldType + "();\r\n"); //Update save models (see save method) saveModels.append("\r\n"); saveModels.append(" //Save " + fieldName + "\r\n"); saveModels.append(" ArrayList<Long> " + idArray + " = new ArrayList<>();\r\n"); saveModels.append(" for (" + modelName + " obj : this." + fieldName + "){\r\n"); saveModels.append(" obj.save();\r\n"); saveModels.append(" " + idArray + ".add(obj.getID());\r\n"); saveModels.append(" }\r\n"); saveModels.append("\r\n\r\n"); saveModels.append(" //Link " + fieldName + " to this " + this.name + "\r\n"); saveModels.append(" target = \"" + tableName + " where " + leftColumn + "=\" + this.id;\r\n"); saveModels.append(" conn.execute(\"delete from \" + target);\r\n"); saveModels.append(" try (javaxt.sql.Recordset rs = conn.getRecordset(\"select * from \" + target, false)){\r\n"); saveModels.append(" for (long " + id + " : " + idArray + "){\r\n"); saveModels.append(" rs.addNew();\r\n"); saveModels.append(" rs.setValue(\"" + leftColumn + "\", this.id);\r\n"); saveModels.append(" rs.setValue(\"" + rightColumn + "\", " + id + ");\r\n"); saveModels.append(" rs.update();\r\n"); saveModels.append(" }\r\n"); saveModels.append(" }\r\n"); } //Update json constructor if (!field.isArray()){ if (!field.isModel()){ if (!password){ if (fieldType.equals("Geometry")){ getJson.append(" try {\r\n"); getJson.append(" this."); getJson.append(fieldName); getJson.append(" = new WKTReader().read(json.get(\""); getJson.append(columnName); getJson.append("\").toString());\r\n"); getJson.append(" }\r\n"); getJson.append(" catch(Exception e) {}\r\n"); } else if (fieldType.equals("byte[]")){ getJson.append(" this."); getJson.append(fieldName); getJson.append(" = json.get(\""); getJson.append(fieldName); getJson.append("\").toByteArray();\r\n"); } else{ if (fieldType.endsWith("[]")){ //e.g. String[] getJson.append(" {"); //Object[] v = json.has("recentCustomers") ? json.get("recentCustomers").toJSONArray().toArray() : null; getJson.append("Object[] v = json.has(\""); getJson.append(fieldName); getJson.append("\") ? json.get(\""); getJson.append(fieldName); getJson.append("\").toJSONArray().toArray() : null;\r\n"); //this.recentCustomers = java.util.Arrays.copyOf(v, v.length, String[].class); getJson.append(" this."); getJson.append(fieldName); getJson.append(" = v==null ? null : java.util.Arrays.copyOf(v, v.length, "); getJson.append(fieldType); getJson.append(".class);"); getJson.append("}\r\n"); } else{ getJson.append(" this."); getJson.append(fieldName); getJson.append(" = json.get(\""); getJson.append(fieldName); getJson.append("\").to"); getJson.append(fieldType); getJson.append("();\r\n"); } } } else{ getJson.append(" this.set"); getJson.append(methodName); getJson.append("(json.get(\""); getJson.append(columnName); getJson.append("\").toString());\r\n"); } } else{ //Model //if (json.has("account")){... getJson.append(" if (json.has(\""); getJson.append(fieldName); getJson.append("\")){\r\n "); getJson.append(fieldName); getJson.append(" = new "); getJson.append(fieldType); getJson.append("(json.get(\""); getJson.append(fieldName); getJson.append("\").toJSONObject());\r\n"); getJson.append(" }\r\n"); //if (json.has("accountID")){... getJson.append(" else if (json.has(\""); getJson.append(fieldName); getJson.append("ID\")){\r\n"); getJson.append(" "); getJson.append("try{\r\n"); getJson.append(" "); getJson.append(fieldName); getJson.append(" = new "); getJson.append(fieldType); getJson.append("(json.get(\""); getJson.append(fieldName); getJson.append("ID\").toLong());\r\n"); getJson.append(" "); getJson.append("}\r\n"); getJson.append(" "); getJson.append("catch(Exception e){}\r\n"); getJson.append(" }\r\n"); } } else{ getJson.append("\r\n"); getJson.append(" //Set " + fieldName + "\r\n"); getJson.append(" if (json.has(\""); getJson.append(fieldName); getJson.append("\")){\r\n"); getJson.append(" for (JSONValue _"); getJson.append(fieldName); getJson.append(" : json.get(\""); getJson.append(fieldName); getJson.append("\").toJSONArray()){\r\n"); getJson.append(" "); getJson.append(fieldName); getJson.append(".add(new " + modelName + "(_" + fieldName + ".toJSONObject()));\r\n"); getJson.append(" }\r\n"); getJson.append(" }\r\n\r\n"); } //Update toJson method if (!field.isArray()){ if (password){ if (toJson.length()==0) toJson.append("\r\n"); toJson.append(" json.set(\""); toJson.append(fieldName); toJson.append("\", null"); toJson.append(");\r\n"); } } } //Update the database constructor with hasMany variables if (hasMany.length()>0){ getValues.append("\r\n\r\n"); getValues.append(" try (javaxt.sql.Connection conn = getConnection(this.getClass())) {\r\n"); getValues.append(hasMany); getValues.append(" }\r\n"); } //Generate list of field names used in the init() method StringBuilder fieldNames = new StringBuilder("\r\n \"id\""); for (Field field : fields){ String fieldType = field.getType(); String columnName = field.getColumnName(); fieldNames.append(", "); fieldNames.append("\r\n "); fieldNames.append("\""); if (fieldType.equals("Geometry")){ fieldNames.append("ST_AsText(" + columnName + ") as "); } fieldNames.append(columnName); fieldNames.append("\""); } //Replace keys in the class template str = str.replace("${fieldMap}", fieldMap.toString()); str = str.replace("${fieldNames}", fieldNames.toString()); str = str.replace("${field[0]}", fields.get(0).getColumnName()); str = str.replace("${initArrays}", initArrays.toString().trim()); str = str.replace("${privateFields}", privateFields.toString().trim()); str = str.replace("${publicMembers}", (getSecurityPrincipalName + publicMembers.toString()).trim()); str = str.replace("${getModels}", getModels.toString()); str = str.replace("${getValues}", getValues.toString()); str = str.replace("${getJson}", getJson.toString().trim()); //Add custom save method as needed if (saveModels.length()>0){ String fn = "\r\n" + " //**************************************************************************\r\n" + " //** save\r\n" + " //**************************************************************************\r\n" + " /** Used to save a " + name + " in the database.\r\n" + " */\r\n" + " public void save() throws SQLException {\r\n\r\n" + " //Update record in the " + this.tableName + " table\r\n" + " super.save();\r\n\r\n\r\n" + " //Save models\r\n" + " try (javaxt.sql.Connection conn = getConnection(this.getClass())) {\r\n" + " String target;\r\n" + " " + saveModels + " }\r\n" + " }\r\n"; str = str.replace("${saveModel}", fn); } else{ str = str.replace("${saveModel}", ""); } //Add custom toJson method as needed if (toJson.length()>0){ String fn = "\r\n" + " //**************************************************************************\r\n" + " //** toJson\r\n" + " //**************************************************************************\r\n" + " /** Returns a string representation of the " + name + " in JSON notation.\r\n" + " */\r\n" + " public JSONObject toJson(){\r\n" + " JSONObject json = super.toJson();\r\n" + " " + toJson + "\r\n" + " return json;\r\n" + " }\r\n"; str = str.replace("${toJson}", fn); } else{ str = str.replace("${toJson}", ""); } //Add includes if (includes.isEmpty()){ str = str.replace("${includes}", ""); } else{ StringBuilder s = new StringBuilder(); for (String include : includes){ s.append("import "); s.append(include); s.append(";\r\n"); } str = str.replace("${includes}", s.toString().trim()); } //Special case for Models with a lastModified field str = str.replace("${getLastModified}", getLastModified); return str; } //************************************************************************** //** getTableSQL //************************************************************************** /** Returns an SQL script used to create a table for the model. The schema * is targeted for a PostgreSQL database. */ public String getTableSQL(){ //Begin create table script StringBuilder str = new StringBuilder(); str.append("CREATE TABLE "); str.append(escapedTableName); str.append(" (\r\n"); str.append(" ID BIGSERIAL NOT NULL,\r\n"); //Add fields ArrayList<String> foreignKeys = new ArrayList<>(); Iterator<Field> it = fields.iterator(); while (it.hasNext()){ Field field = it.next(); if (field.isArray()) continue; str.append(" "); str.append(field.getColumnName().toUpperCase()); str.append(" "); str.append(field.getColumnType()); if (field.isRequired()) str.append(" NOT NULL"); if (field.hasDefaultValue()){ Object defaultValue = field.getDefaultValue(); str.append(" DEFAULT "); if (defaultValue instanceof String){ String val = (String) defaultValue; if (val.contains("(") && val.endsWith(")")){ str.append(val); } else{ str.append("'" + val.replace("'", "''") + "'"); } } else{ str.append(defaultValue.toString()); } } if (field.isUnique()) str.append(" UNIQUE"); str.append(","); str.append("\r\n"); ForeignKey foreignKey = field.getForeignKey(); if (foreignKey!=null) foreignKeys.add(foreignKey.getColumnName()); } //Add primary key constraint str.append(" CONSTRAINT PK_"); str.append(this.tableName.toUpperCase()); str.append(" PRIMARY KEY (ID)"); //End create table script str.append("\r\n);\r\n\r\n"); return str.toString(); } //************************************************************************** //** getDiamondTableSQL //************************************************************************** /** Returns an SQL script used to generate diamond tables and indexes */ public String getDiamondTableSQL(){ StringBuilder str = new StringBuilder(); Iterator<Field> it = fields.iterator(); while (it.hasNext()){ Field field = it.next(); if (field.isArray()){ String modelName = field.getType().substring(10); modelName = modelName.substring(0, modelName.length()-1); String leftTable = this.tableName.toUpperCase(); String leftColumn = leftTable + "_ID"; String rightTable = Utils.camelCaseToUnderScore(modelName).toUpperCase(); String rightColumn = rightTable + "_ID"; //Special case for when a model hasMany of itself if (leftTable.equals(rightTable)) rightColumn = rightTable + "_ID2"; String tableName = leftTable + "_" + rightTable; //String tableName = leftTable + "_" + field.getColumnName().toUpperCase(); String foreignKey = "FK_" + tableName; String indexPrefix = "IDX_" + tableName.toUpperCase()+ "_"; if (schemaName!=null) tableName = escapedSchemaName + "." + tableName; leftTable = escapeTableName(leftTable); if (schemaName!=null) leftTable = escapedSchemaName + "." + leftTable; rightTable = escapeTableName(rightTable); if (schemaName!=null) rightTable = escapedSchemaName + "." + rightTable; str.append("CREATE TABLE "); str.append(tableName); str.append(" (\r\n "); str.append(leftColumn); str.append(" BIGINT NOT NULL,\r\n "); str.append(rightColumn); str.append(" BIGINT NOT NULL,\r\n"); str.append(" CONSTRAINT "); str.append(foreignKey); str.append(" FOREIGN KEY ("); str.append(leftColumn); str.append(") REFERENCES "); str.append(leftTable); str.append("(ID)\r\n"); str.append(" ON DELETE CASCADE ON UPDATE NO ACTION\r\n"); str.append(");\r\n\r\n"); str.append("ALTER TABLE "); str.append(tableName); str.append(" ADD FOREIGN KEY ("); str.append(rightColumn); str.append(") REFERENCES "); str.append(rightTable); str.append("(ID)\r\n"); str.append(" ON DELETE CASCADE ON UPDATE NO ACTION;\r\n\r\n"); str.append("CREATE INDEX "); str.append(indexPrefix); str.append(leftColumn); str.append(" ON "); str.append(tableName); str.append("("); str.append(leftColumn); str.append(");\r\n"); str.append("CREATE INDEX "); str.append(indexPrefix); str.append(rightColumn); str.append(" ON "); str.append(tableName); str.append("("); str.append(rightColumn); str.append(");\r\n"); str.append("\r\n"); } } return str.toString(); } //************************************************************************** //** getForeignKeySQL //************************************************************************** /** Returns an SQL script used to add foreign keys to a table associated * with the model. Ordinarily, the foreign keys are defined inline with the * create table script. However, that would require creating tables in a * proper sequence which is difficult to do. Instead, it is easier to * create all the tables first, then update the tables by adding a foreign * key constraint. */ public String getForeignKeySQL(){ StringBuilder str = new StringBuilder(); Iterator<Field> it = fields.iterator(); while (it.hasNext()){ Field field = it.next(); if (!field.isArray()){ ForeignKey foreignKey = field.getForeignKey(); if (foreignKey!=null){ String columnName = foreignKey.getColumnName().toUpperCase(); String foreignTable = escapeTableName(foreignKey.getForeignTable().toUpperCase()); if (schemaName!=null) foreignTable = escapedSchemaName + "." + foreignTable; String foreignColumn = "ID"; str.append("ALTER TABLE "); str.append(escapedTableName); str.append(" ADD FOREIGN KEY ("); str.append(columnName); str.append(") REFERENCES "); str.append(foreignTable); str.append("("); str.append(foreignColumn); str.append(")\r\n"); str.append(" ON DELETE "); str.append(foreignKey.onDelete()); str.append(" ON UPDATE NO ACTION;\r\n\r\n"); } } else{ //Handled by getDiamondTableSQL() } } return str.toString(); } //************************************************************************** //** getIndexSQL //************************************************************************** /** Returns an SQL script used to add indexes to the table associated with * the model. */ public String getIndexSQL(){ StringBuilder str = new StringBuilder(); String indexPrefix = "IDX_" + tableName.toUpperCase()+ "_"; Iterator<Field> it = fields.iterator(); while (it.hasNext()){ Field field = it.next(); if (!field.isArray()){ ForeignKey foreignKey = field.getForeignKey(); if (foreignKey!=null){ String columnName = foreignKey.getColumnName().toUpperCase(); String foreignTable = foreignKey.getForeignTable().toUpperCase(); str.append("CREATE INDEX "); str.append(indexPrefix); str.append(foreignTable); str.append(" ON "); str.append(escapedTableName); str.append("("); str.append(columnName); str.append(");\r\n"); } else{ String columnName = field.getColumnName().toUpperCase(); if (field.getColumnType().startsWith("geo")){ str.append("CREATE INDEX "); str.append(indexPrefix); str.append(columnName); str.append(" ON "); str.append(escapedTableName); str.append(" USING GIST("); str.append(columnName); str.append(");\r\n"); } } } else{ //Handled by getDiamondTableSQL() } } return str.toString(); } //************************************************************************** //** getTriggerSQL //************************************************************************** /** Returns an SQL script used to generate triggers */ public String getTriggerSQL(){ StringBuilder str = new StringBuilder(); //Add last modified trigger as needed. See Writer.write() for the //last_modified() implementation. if (hasLastModifiedField()){ str.append("CREATE TRIGGER TGR_"); str.append(this.tableName.toUpperCase()); str.append("_UPDATE BEFORE INSERT OR UPDATE ON "); str.append(escapedTableName); str.append("\r\n FOR EACH ROW EXECUTE PROCEDURE last_modified();\r\n\r\n"); } return str.toString(); } //************************************************************************** //** hasLastModifiedField //************************************************************************** /** Returns true if the model contains a lastModified date field. */ protected boolean hasLastModifiedField(){ Iterator<Field> it = fields.iterator(); while (it.hasNext()){ Field field = it.next(); if (field.isArray()) continue; if (field.isLastModifiedDate()) return true; } return false; } //************************************************************************** //** toString //************************************************************************** /** Returns the name of this model. */ public String toString(){ return getName(); } //************************************************************************** //** escapeTableName //************************************************************************** /** Returns a sql compatible table name */ protected String escapeTableName(String tableName){ if (tableName==null) return null; if (tableName.equalsIgnoreCase("user") && schemaName==null){ //and db is postgres return "\"" + tableName.toLowerCase() + "\""; } else{ return tableName.toUpperCase(); } } //************************************************************************** //** getTemplate //************************************************************************** /** Static method used to extract the class template (Class.txt) found in * this package. */ private static String getTemplate(){ javaxt.io.Jar jar = new javaxt.io.Jar(javaxt.orm.Model.class); javaxt.io.Jar.Entry entry = jar.getEntry("javaxt.orm", "Class.txt"); return entry.getText(); } } |