package io.trygvis.persistence; import io.trygvis.container.compiler.NotImplementedException; import io.trygvis.container.compiler.SqlUnitModel; import io.trygvis.container.compiler.model.ClassG; import io.trygvis.container.compiler.model.Imports; import io.trygvis.container.compiler.model.MethodRef; import io.trygvis.container.compiler.model.Parameters; import io.trygvis.container.compiler.model.TypeRef; import io.trygvis.persistence.sql.AbstractTypedQuery; import javax.persistence.TypedQuery; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.List; import static io.trygvis.container.compiler.model.Parameters.ParameterRef; import static io.trygvis.persistence.FieldMirror.PrimitiveFieldMirror; import static io.trygvis.persistence.FieldMirror.ReferenceFieldMirror; import static java.lang.String.format; import static java.lang.reflect.Modifier.PUBLIC; import static java.lang.reflect.Modifier.STATIC; import static java.util.Collections.singletonList; import static org.apache.commons.lang.StringUtils.join; public class EntityMirror implements Comparable { public final GeneratorConfiguration generatorConfiguration; public final List fields = new ArrayList<>(); public final List idFields = new ArrayList<>(); public final TypeRef type; public final String tableName; public final TypeRef daoType; public EntityMirror(GeneratorConfiguration generatorConfiguration, TypeRef type, String tableName) { this.generatorConfiguration = generatorConfiguration; this.type = type; this.tableName = tableName; this.daoType = new TypeRef(type.plainName + "Dao").args(type.args); } public void add(FieldMirror... fields) { for (FieldMirror field : fields) { this.fields.add(field); if (field.id) { this.idFields.add(field); } } } public FieldMirror getIdField() { return idFields.get(0); } public String createTableSql(SqlUnitModel unit) { List columns = new ArrayList<>(); for (FieldMirror field : fields) { String s; if (field instanceof PrimitiveFieldMirror) { TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); s = " " + field.sqlName + " " + typeHandler.sqlType(field); if (field.id) { s += " PRIMARY KEY"; } else if (field.notNull) { s += " NOT NULL"; } else if (field.unique) { s += " UNIQUE"; } } else if (field instanceof ReferenceFieldMirror) { ReferenceFieldMirror ref = (ReferenceFieldMirror) field; EntityMirror referenced = unit.get(ref.type); if(referenced.idFields.size() == 1) { FieldMirror idField = referenced.idFields.get(0); TypeHandler typeHandler = generatorConfiguration.typeHandler(idField.type); s = " " + field.sqlName + " " + typeHandler.sqlType(field); s += " REFERENCES " + referenced.tableName + "(" + idField.sqlName + ")"; if (field.notNull) { s += " NOT NULL"; } else if (field.unique) { s += " UNIQUE"; } } else { throw new NotImplementedException(); } } else { throw new RuntimeException("Unknown field type: " + field.getClass()); } columns.add(s); } return format("CREATE TABLE " + tableName + "(%n" + join(columns, ",%n") + "%n);"); } public String dropTableSql() { return "DROP TABLE " + tableName + ";"; } public String insertIntoSql() { List names = new ArrayList<>(); List placeholders = new ArrayList<>(); for (FieldMirror field : fields) { names.add(field.sqlName); placeholders.add("?"); } return "INSERT INTO " + tableName + "(" + join(names, ", ") + ") " + "VALUES(" + join(placeholders, ", ") + ");"; } public String deleteFromSql() { List ss = new ArrayList<>(); for (FieldMirror field : idFields) { ss.add(field.sqlName + "=?"); } return "DELETE FROM " + tableName + " WHERE " + join(ss, " AND ") + ";"; } public String defaultFields() { List names = new ArrayList<>(); for (FieldMirror field : fields) { names.add(field.sqlName); } return join(names, ", "); } public MethodRef insertInto(SqlUnitModel unit, Imports imports) { TypeRef sqlExceptionType = imports.add(SQLException.class); TypeRef typesType = imports.add(Types.class); TypeRef conType = imports.add(Connection.class); TypeRef psType = imports.add(PreparedStatement.class); Parameters p = new Parameters(); ParameterRef con = p.addParameter(conType, "con"); ParameterRef o = p.addParameter(type, "o"); List body = new ArrayList<>(); body.add("try(" + psType + " stmt = " + con.name + ".prepareStatement(insertIntoSql)) {"); for (int i = 0; i < fields.size(); i++) { FieldMirror field = fields.get(i); if(field instanceof PrimitiveFieldMirror) { TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); String access = o.name + "." + field.javaName; String setter = " stmt." + typeHandler.setter(i + 1, access) + ";"; if(field.notNull) { body.add(setter); } else { body.add(" " + field.type + " " + field.javaName + " = " + access + ";"); body.add(" if(" + field.javaName + " == null) {"); body.add(" stmt.setNull(" + (i + 1) + ", " + typesType + "." + typeHandler.typeName() + ");"); body.add(" } else {"); body.add(" " + setter); body.add(" }"); } } else if (field instanceof ReferenceFieldMirror) { ReferenceFieldMirror ref = (ReferenceFieldMirror) field; EntityMirror referenced = unit.get(ref.type); FieldMirror idField = referenced.getIdField(); TypeHandler typeHandler = generatorConfiguration.typeHandler(idField.type); body.add(" stmt." + typeHandler.setter(i + 1, o.name + "." + field.javaName + "." + idField.javaName) + ";"); } } body.add(" stmt.executeUpdate();"); body.add("}"); return new MethodRef(PUBLIC | STATIC, TypeRef.VOID, "insertInto", p, body).exception(sqlExceptionType); } public MethodRef delete(Imports imports) { TypeRef conType = imports.add(Connection.class); TypeRef objectType = imports.add(type); Parameters p = new Parameters(); ParameterRef con = p.addParameter(conType, "con"); ParameterRef o = p.addParameter(objectType, "o"); List arguments = new ArrayList<>(); arguments.add(con.name); for (FieldMirror field : idFields) { arguments.add(o.name + "." + field.javaName); } List body = new ArrayList<>(); body.add("deleteById(" + join(arguments, ", ") + ");"); return new MethodRef(PUBLIC | STATIC, TypeRef.VOID, "delete", p, body). exception(imports.add(SQLException.class)); } public MethodRef deleteById(Imports imports) { TypeRef conType = imports.add(Connection.class); TypeRef psType = imports.add(PreparedStatement.class); Parameters p = new Parameters(); ParameterRef con = p.addParameter(conType, "con"); List body = new ArrayList<>(); body.add("try(" + psType + " stmt = " + con.name + ".prepareStatement(deleteFromSql)) {"); for (int i = 0; i < idFields.size(); i++) { FieldMirror field = idFields.get(i); p.addParameter(field.type, field.javaName); TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); body.add(" stmt." + typeHandler.setter(i + 1, field.javaName) + ";"); } body.add(" stmt.executeUpdate();"); body.add("}"); return new MethodRef(PUBLIC | STATIC, TypeRef.VOID, "deleteById", p, body). exception(imports.add(SQLException.class)); } public ClassG queryType(Imports imports) { TypeRef abstractQueryType = imports.add(AbstractTypedQuery.class).args(type); TypeRef conType = imports.add(Connection.class); TypeRef entityTypedQuery = new TypeRef(type.className + "TypedQuery"); Parameters p = new Parameters(); ParameterRef c = p.addParameter(conType, "c"); ClassG typedQuery = new ClassG(PUBLIC | STATIC, entityTypedQuery). extendsType(abstractQueryType); typedQuery.addConstructor(p, singletonList("super(" + c.name + ", " + daoType.className + ".desc);")); return typedQuery; } public MethodRef query(SqlUnitModel sqlUnit, Imports imports) { TypeRef conType = imports.add(Connection.class); TypeRef typedQueryType = imports.add(TypedQuery.class).args(type); TypeRef entityTypedQuery = new TypeRef(type.className + "TypedQuery"); Parameters p = new Parameters(); ParameterRef c = p.addParameter(conType, "c"); List body = new ArrayList<>(); body.add("return new " + entityTypedQuery + "(" + c.name + ");"); return new MethodRef(PUBLIC | STATIC, typedQueryType, "query", p, body); } public MethodRef fromResultSet(SqlUnitModel unit, Imports g) { TypeRef rsType = g.add(ResultSet.class); Parameters p = new Parameters(); ParameterRef rs = p.addParameter(rsType, "rs"); List body = new ArrayList<>(); List names = new ArrayList<>(); for (int i = 0; i < fields.size(); i++) { FieldMirror field = fields.get(i); if(field instanceof PrimitiveFieldMirror) { TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); body.add(field.type + " " + field.javaName + " = " + typeHandler.getter(rs.name, i + 1) + ";"); } else if (field instanceof ReferenceFieldMirror) { // ReferenceFieldMirror ref = (ReferenceFieldMirror) field; // EntityMirror referenced = unit.get(ref.type); // FieldMirror idField = referenced.getIdField(); // TypeHandler typeHandler = generatorConfiguration.typeHandler(idField.type); // body.add(field.type + " " + field.javaName + " = " + typeHandler.getter(rs.name, i + 1) + ";"); body.add(field.type + " " + field.javaName + " = null;"); } names.add(field.javaName); } body.add("return new " + type + "(" + join(names, ", ") + ");"); return new MethodRef(PUBLIC, type, "fromResultSet", p, body). exception(g.add(SQLException.class)); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof EntityMirror)) return false; EntityMirror that = (EntityMirror) o; return type.equals(that.type); } @Override public int hashCode() { return type.hashCode(); } @Override public int compareTo(@SuppressWarnings("NullableProblems") EntityMirror o) { return type.compareTo(o.type); } }