diff options
15 files changed, 870 insertions, 456 deletions
diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/EntityHandler.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/EntityHandler.java index 1dbc534..a2bb5a6 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/EntityHandler.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/EntityHandler.java @@ -1,10 +1,5 @@ package io.trygvis.container.compiler; -import io.trygvis.container.compiler.model.ClassG; -import io.trygvis.container.compiler.model.Constructor; -import io.trygvis.container.compiler.model.FieldRef; -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.EntityMirror; import io.trygvis.persistence.FieldMirror; @@ -13,8 +8,7 @@ import io.trygvis.persistence.SequenceMirror; import io.trygvis.persistence.SqlEntity; import io.trygvis.persistence.SqlEntitySet; import io.trygvis.persistence.TypeHandler; -import io.trygvis.persistence.sql.SqlDao; -import io.trygvis.persistence.sql.SqlEntityMeta; +import io.trygvis.persistence.generators.DaoGenerator; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.AnnotationMirror; @@ -30,10 +24,6 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.persistence.Id; import javax.persistence.SequenceGenerator; -import java.io.IOException; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -41,9 +31,8 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import static io.trygvis.container.compiler.Utils.*; -import static io.trygvis.container.compiler.model.Parameters.ParameterRef; -import static io.trygvis.container.compiler.model.TypeRef.VOID; +import static io.trygvis.container.compiler.Utils.toFieldName; +import static io.trygvis.container.compiler.Utils.writeFile; import static io.trygvis.persistence.FieldMirror.AccessorType.FIELD; import static io.trygvis.persistence.FieldMirror.AccessorType.METHOD; import static io.trygvis.persistence.FieldMirror.FieldType.PRIMITIVE; @@ -51,7 +40,6 @@ import static io.trygvis.persistence.FieldMirror.FieldType.REFERENCE; import static io.trygvis.persistence.TypeHandler.StringEnumTypeHandler; import static java.lang.Character.isUpperCase; import static java.lang.String.format; -import static java.lang.reflect.Modifier.PUBLIC; import static javax.lang.model.util.ElementFilter.fieldsIn; import static javax.lang.model.util.ElementFilter.methodsIn; import static org.apache.commons.lang.StringUtils.join; @@ -237,88 +225,6 @@ public class EntityHandler extends AbstractHandler { } } - public ClassG phase3(EntityMirror entityMirror) throws IOException { - ClassG g = new ClassG(PUBLIC, entityMirror.daoType). - extendsType(new TypeRef(SqlDao.class).args(entityMirror.idType, entityMirror.type)); - Parameters p = new Parameters(); - ParameterRef c = p.addParameter(new TypeRef(Connection.class), "c"); - g.add(new Constructor(p, "super(" + c.name + ");")); - - TypeRef stringType = g.imports.add(String.class); - TypeRef sqlEntityDescType = g.imports.add(SqlEntityMeta.class); - TypeRef sqlException = g.imports.add(SQLException.class); - - FieldRef createTableSql = g.addPublicStaticFinalField(stringType, "createTableSql"). - value(toJavaString(entityMirror.createTableSql(sqlUnit))); - g.add(new MethodRef(PUBLIC, stringType, "createTableSql", "return createTableSql;")); - FieldRef dropTableSql = g.addPublicStaticFinalField(stringType, "dropTableSql"). - value(toJavaString(entityMirror.dropTableSql())); - g.add(new MethodRef(PUBLIC, stringType, "dropTableSql", "return dropTableSql;")); - g.addPublicStaticFinalField(stringType, "insertIntoSql"). - value(toJavaString(entityMirror.insertIntoSql(sqlUnit))); - g.addPublicStaticFinalField(stringType, "deleteFromSql"). - value(toJavaString(entityMirror.deleteFromSql())); - String desc = "new " + sqlEntityDescType + "(" + - toJavaString(entityMirror.tableName) + ", " + - toJavaString(entityMirror.defaultFields()) + ", " + - createTableSql.name + ", " + - dropTableSql.name + - ")"; - g.addPublicStaticFinalField(sqlEntityDescType, "desc").value(desc); - ClassG.InnerClassG typedQuery = g.addInnerClass(entityMirror.queryType(g.imports)); - g.addInnerClass(entityMirror.utils(sqlUnit)); - - { - p = new Parameters(); - ParameterRef rs = p.addParameter(new TypeRef(ResultSet.class), "rs"); - g.add(new MethodRef(PUBLIC, entityMirror.type, "fromResultSet", p, - "return Utils.fromResultSet" + entityMirror.type.className + "(" + rs.name + ");"). - exception(sqlException)); - } - - { - p = new Parameters(); - ParameterRef entity = p.addParameter(entityMirror.type, "entity"); - g.add(new MethodRef(PUBLIC, VOID, "insert", p, - "Utils.insert" + entityMirror.type.className + "(super.c, " + entity.name + ");"). - exception(sqlException)); - } - - { - p = new Parameters(); - ParameterRef id = p.addParameter(entityMirror.idType, "id"); - g.add(new MethodRef(PUBLIC, entityMirror.type, "selectById", p, - "return Utils.select" + entityMirror.type.className + "ById(super.c, " + id.name + ");"). - exception(sqlException)); - } - - { - p = new Parameters(); - ParameterRef entity = p.addParameter(entityMirror.type, "entity"); - g.add(new MethodRef(PUBLIC, VOID, "delete", p, - "Utils.delete" + entityMirror.type.className + "(super.c, " + entity.name + ");"). - exception(sqlException)); - } - - { - p = new Parameters(); - ParameterRef id = p.addParameter(entityMirror.idType, "id"); - g.add(new MethodRef(PUBLIC, VOID, "deleteById", p, - "Utils.delete" + entityMirror.type.className + "ById(super.c, " + id.name + ");"). - exception(sqlException)); - } - - { - p = new Parameters(); - ParameterRef entity = p.addParameter(entityMirror.type, "entity"); - g.add(new MethodRef(PUBLIC, VOID, "update", p, - "Utils.update" + entityMirror.type.className + "(super.c, " + entity.name + ");"). - exception(sqlException)); - } - - return g; - } - public FieldMirror fromElement(GeneratorConfiguration generatorConfiguration, VariableElement var, ExecutableElement getter, ExecutableElement setter) { // TODO: check the setter for annotations too @@ -401,7 +307,8 @@ public class EntityHandler extends AbstractHandler { public void phase3(boolean errorRaised) throws Exception { try { for (EntityMirror entity : sqlUnit.getEntities().values()) { - writeFile(processingEnv, phase3(entity), sqlUnit.element(entity)); + DaoGenerator daoGenerator = new DaoGenerator(generatorConfiguration, sqlUnit, entity); + writeFile(processingEnv, daoGenerator.generate(), sqlUnit.element(entity)); } } catch (CompilerException | InternalErrorException e) { // Ignore any exceptions if we had an error from before diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java index 64ac678..1272759 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java @@ -1,8 +1,10 @@ package io.trygvis.container.compiler; import io.trygvis.container.log.Log; +import io.trygvis.persistence.EntityMirror; import io.trygvis.persistence.SqlEntity; import io.trygvis.persistence.SqlEntitySet; +import io.trygvis.persistence.generators.DaoGenerator; import org.springframework.transaction.annotation.Transactional; import javax.annotation.processing.Completion; @@ -26,9 +28,11 @@ import java.util.HashSet; import java.util.Set; import static io.trygvis.container.compiler.Utils.writeFile; -import static io.trygvis.persistence.generators.EntityManagerGenerator.generateEntityManager; import static io.trygvis.persistence.generators.EntityManagerFactoryGenerator.generateEntityManagerFactory; +import static io.trygvis.persistence.generators.EntityManagerGenerator.generateEntityManager; import static io.trygvis.persistence.generators.SequencesGenerator.generateSequences; +import static io.trygvis.persistence.generators.SqlSessionGenerator.generateSqlSession; +import static io.trygvis.persistence.generators.SqlSessionFactoryGenerator.generateSqlSessionFactory; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.emptySet; @@ -144,6 +148,8 @@ public class MyProcessor implements Processor { writeFile(processingEnv, generateSequences(unit), null); writeFile(processingEnv, generateEntityManagerFactory(unit), null); writeFile(processingEnv, generateEntityManager(unit), null); + writeFile(processingEnv, generateSqlSession(unit), null); + writeFile(processingEnv, generateSqlSessionFactory(unit), null); return true; } diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java index fe595a0..e9b5a47 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java @@ -93,6 +93,11 @@ public class ClassG { return addField(PUBLIC | STATIC | FINAL, type, name); } + public ClassG add(FieldRef fieldRef) { + this.fields.add(fieldRef); + return this; + } + public ClassG add(Constructor constructor) { constructors.add(constructor); return this; diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/EntityMirror.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/EntityMirror.java index 140a910..f63c4b2 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/persistence/EntityMirror.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/EntityMirror.java @@ -2,34 +2,14 @@ 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.Constructor; -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.Utils.toGetterName; -import static io.trygvis.container.compiler.Utils.toSetterName; -import static io.trygvis.container.compiler.model.Parameters.ParameterRef; -import static io.trygvis.persistence.FieldMirror.AccessorType.FIELD; -import static io.trygvis.persistence.FieldMirror.AccessorType.METHOD; import static io.trygvis.persistence.FieldMirror.FieldType.PRIMITIVE; import static io.trygvis.persistence.FieldMirror.FieldType.REFERENCE; 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<EntityMirror> { @@ -48,7 +28,7 @@ public class EntityMirror implements Comparable<EntityMirror> { this.tableName = tableName; this.daoType = new TypeRef(type.plainName + "Dao").args(type.args); - this.utilsType = new TypeRef(type.plainName + ".Utils").args(type.args); + this.utilsType = new TypeRef(type.plainName + "Dao.Utils").args(type.args); } public void add(FieldMirror... fields) { @@ -69,281 +49,6 @@ public class EntityMirror implements Comparable<EntityMirror> { return idFields.get(0); } - public String createTableSql(SqlUnitModel unit) { - List<String> columns = new ArrayList<>(); - for (FieldMirror field : fields) { - String s; - if (field.fieldType == PRIMITIVE) { - 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.fieldType == REFERENCE) { - EntityMirror referenced = unit.get(field.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(SqlUnitModel unit) { - List<String> columns = new ArrayList<>(); - List<String> values = new ArrayList<>(); - for (FieldMirror field : fields) { - columns.add(field.sqlName); - if (field.id) { - values.add("nextval('" + unit.getDefaultSequence().sequenceName + "')"); - } else { - values.add("?"); - } - } - - return "INSERT INTO " + tableName + "(" + join(columns, ", ") + ") " + - "VALUES(" + join(values, ", ") + ");"; - } - - public String deleteFromSql() { - List<String> ss = new ArrayList<>(); - for (FieldMirror field : idFields) { - ss.add(field.sqlName + "=?"); - } - - return "DELETE FROM " + tableName + " WHERE " + join(ss, " AND ") + ";"; - } - - public String defaultFields() { - List<String> 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<String> body = new ArrayList<>(); - - body.add("try(" + psType + " stmt = " + con.name + ".prepareStatement(insertIntoSql)) {"); - int i = 0; - for (FieldMirror field : fields) { - // Assume all ID fields are generated for now. - if (field.id) { - continue; - } - - i++; - - TypeHandler typeHandler; - String accessor; - String setter; - if (field.fieldType == PRIMITIVE) { - typeHandler = generatorConfiguration.typeHandler(field.type); - accessor = field.fieldAccessor(o); - setter = " stmt." + typeHandler.setter(i, accessor) + ";"; - } else { - EntityMirror referenced = unit.get(field.type); - FieldMirror idField = referenced.getIdField(); - typeHandler = generatorConfiguration.typeHandler(idField.type); - accessor = field.referenceAccessor(o, idField); - setter = " stmt." + typeHandler.setter(i, accessor) + ";"; - accessor = "null"; - } - - if (field.notNull) { - body.add(setter); - } else { - body.add(" " + field.type + " " + field.javaName + " = " + accessor + ";"); - body.add(" if (" + field.javaName + " == null) {"); - body.add(" stmt.setNull(" + i + ", " + typesType + "." + typeHandler.typeName() + ");"); - body.add(" } else {"); - body.add(" " + setter); - body.add(" }"); - } - } - body.add(" stmt.executeUpdate();"); - body.add("}"); - return new MethodRef(PUBLIC | STATIC, TypeRef.VOID, "insert" + type.className, p, body).exception(sqlExceptionType); - } - - public MethodRef selectById(Imports imports) { - Parameters p = new Parameters(); - p.addParameter(imports.add(Connection.class), "c"); - p.addParameter(idType, "id"); - return new MethodRef(PUBLIC | STATIC, type, "select" + type.className + "ById", p, - "throw new UnsupportedOperationException(\"Not implemented\");"); - } - - public MethodRef update(Imports imports) { - Parameters p = new Parameters(); - p.addParameter(imports.add(Connection.class), "c"); - p.addParameter(type, "entity"); - return new MethodRef(PUBLIC | STATIC, type, "update" + type.className, p, - "throw new UnsupportedOperationException(\"Not implemented\");"); - } - - 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<String> arguments = new ArrayList<>(); - arguments.add(con.name); - for (FieldMirror field : idFields) { - if (field.accessorType == FIELD) { - arguments.add(o.name + "." + field.javaName); - } else { - arguments.add(o.name + "." + toGetterName(field.javaName) + "()"); - } - } - - return new MethodRef(PUBLIC | STATIC, TypeRef.VOID, "delete" + type.className, p, - "delete" + type.className + "ById(" + join(arguments, ", ") + ");").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<String> 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, "delete" + type.className + "ById", p, body). - exception(imports.add(SQLException.class)); - } - - public ClassG queryType(Imports imports) { - TypeRef sqlQueryType = imports.add(AbstractTypedQuery.class).args(type); - TypeRef conType = imports.add(Connection.class); - TypeRef entityTypedQuery = new TypeRef(type.className + "TypedQuery"); - TypeRef sqlExceptionType = new TypeRef(SQLException.class); - - Parameters p = new Parameters(); - ParameterRef c = p.addParameter(conType, "c"); - Constructor constructor = new Constructor(p, singletonList("super(" + c.name + ", " + daoType.className + ".desc);")); - ClassG g = new ClassG(PUBLIC | STATIC, entityTypedQuery). - extendsType(sqlQueryType). - add(constructor); - p = new Parameters(); - ParameterRef rs = p.addParameter(new TypeRef(ResultSet.class), "rs"); - MethodRef fromResultSet = new MethodRef(PUBLIC, type, "fromResultSet", p, - "return " + utilsType.className + ".fromResultSet" + type.className + "(" + rs.name + ");"). - exception(sqlExceptionType); - g.add(fromResultSet); - return g; - } - - public MethodRef query(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"); - return new MethodRef(PUBLIC | STATIC, typedQueryType, "query" + type.className, p, - "return new " + entityTypedQuery + "(" + c.name + ");"); - } - - public MethodRef fromResultSet(Imports g) { - TypeRef rsType = g.add(ResultSet.class); - Parameters p = new Parameters(); - ParameterRef rs = p.addParameter(rsType, "rs"); - - List<String> body = new ArrayList<>(); - List<String> names = new ArrayList<>(); - for (int i = 0; i < fields.size(); i++) { - FieldMirror field = fields.get(i); - if (field.accessorType != FIELD) { - continue; - } - if (field.fieldType == PRIMITIVE) { - TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); - body.add(field.type + " " + field.javaName + " = " + typeHandler.getter(rs.name, i + 1) + ";"); - } else if (field.fieldType == REFERENCE) { -// 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(type + " returnValue = new " + type + "(" + join(names, ", ") + ");"); - - for (int i = 0; i < fields.size(); i++) { - FieldMirror field = fields.get(i); - if (field.accessorType != METHOD) { - continue; - } - if (field.fieldType == PRIMITIVE) { - TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); - body.add("returnValue." + toSetterName(field.javaName) + "(" + typeHandler.getter(rs.name, i + 1) + ");"); - } else if (field.fieldType == REFERENCE) { -// 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("returnValue." + toSetterName(field.javaName) + "(" + null + ");"); - } - names.add(field.javaName); - } - - body.add("return returnValue;"); - - return new MethodRef(PUBLIC | STATIC, type, "fromResultSet" + type.className, p, body). - exception(g.add(SQLException.class)); - } - @Override public boolean equals(Object o) { if (this == o) return true; @@ -363,16 +68,4 @@ public class EntityMirror implements Comparable<EntityMirror> { public int compareTo(@SuppressWarnings("NullableProblems") EntityMirror o) { return type.compareTo(o.type); } - - public ClassG utils(SqlUnitModel unit) { - ClassG g = new ClassG(PUBLIC | STATIC, utilsType); - g.add(insertInto(unit, g.imports)); - g.add(selectById(g.imports)); - g.add(update(g.imports)); - g.add(delete(g.imports)); - g.add(deleteById(g.imports)); - g.add(query(g.imports)); - g.add(fromResultSet(g.imports)); - return g; - } } diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/TypeHandler.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/TypeHandler.java index f13018a..9566ee7 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/persistence/TypeHandler.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/TypeHandler.java @@ -19,9 +19,9 @@ public abstract class TypeHandler { this.nullable = nullable; } - abstract String setter(int i, String expr); + public abstract String setter(int i, String expr); - abstract String getter(String rs, int i); + public abstract String getter(String rs, int i); public String sqlType(FieldMirror field) { return sqlType; @@ -99,12 +99,12 @@ public abstract class TypeHandler { } @Override - String setter(int i, String expr) { + public String setter(int i, String expr) { return "setString(" + i + ", " + expr + ".name())"; } @Override - String getter(String rs, int i) { + public String getter(String rs, int i) { return enumType.plainName + ".valueOf(" + rs + ".getString(" + i + "))"; } } diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoGenerator.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoGenerator.java new file mode 100644 index 0000000..d9ac482 --- /dev/null +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoGenerator.java @@ -0,0 +1,214 @@ +package io.trygvis.persistence.generators; + +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.Constructor; +import io.trygvis.container.compiler.model.FieldRef; +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.EntityMirror; +import io.trygvis.persistence.FieldMirror; +import io.trygvis.persistence.GeneratorConfiguration; +import io.trygvis.persistence.TypeHandler; +import io.trygvis.persistence.sql.SqlDao; +import io.trygvis.persistence.sql.SqlEntityMeta; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import static io.trygvis.container.compiler.Utils.toJavaString; +import static io.trygvis.container.compiler.model.TypeRef.VOID; +import static io.trygvis.persistence.FieldMirror.FieldType.PRIMITIVE; +import static io.trygvis.persistence.FieldMirror.FieldType.REFERENCE; +import static java.lang.String.format; +import static java.lang.reflect.Modifier.PUBLIC; +import static org.apache.commons.lang.StringUtils.join; + +public class DaoGenerator { + + private final GeneratorConfiguration generatorConfiguration; + private final SqlUnitModel unit; + private final EntityMirror entity; + + public DaoGenerator(GeneratorConfiguration generatorConfiguration, SqlUnitModel unit, EntityMirror entity) { + this.generatorConfiguration = generatorConfiguration; + this.unit = unit; + this.entity = entity; + } + + public ClassG generate() throws IOException { + ClassG g = new ClassG(PUBLIC, entity.daoType). + extendsType(new TypeRef(SqlDao.class).args(entity.idType, entity.type)); + Parameters p = new Parameters(); + Parameters.ParameterRef c = p.addParameter(new TypeRef(Connection.class), "c"); + g.add(new Constructor(p, "super(" + c.name + ");")); + + TypeRef stringType = g.imports.add(String.class); + TypeRef sqlEntityDescType = g.imports.add(SqlEntityMeta.class); + TypeRef sqlException = g.imports.add(SQLException.class); + TypeRef listOfEntityType = new TypeRef(List.class).args(entity.type); + + FieldRef createTableSql = g.addPublicStaticFinalField(stringType, "createTableSql"). + value(toJavaString(createTableSql())); + g.add(new MethodRef(PUBLIC, stringType, "createTableSql", "return createTableSql;")); + FieldRef dropTableSql = g.addPublicStaticFinalField(stringType, "dropTableSql"). + value(toJavaString(dropTableSql())); + g.add(new MethodRef(PUBLIC, stringType, "dropTableSql", "return dropTableSql;")); + g.addPublicStaticFinalField(stringType, "insertIntoSql"). + value(toJavaString(insertIntoSql())); + g.addPublicStaticFinalField(stringType, "deleteFromSql"). + value(toJavaString(deleteFromSql())); + String desc = "new " + sqlEntityDescType + "(" + + toJavaString(entity.tableName) + ", " + + toJavaString(defaultFields()) + ", " + + createTableSql.name + ", " + + dropTableSql.name + + ")"; + g.addPublicStaticFinalField(sqlEntityDescType, "desc").value(desc); + DaoUtilsGenerator daoUtil = new DaoUtilsGenerator(unit, generatorConfiguration, entity); + g.addInnerClass(daoUtil.queryType(g.imports)); + g.addInnerClass(daoUtil.utils()); + + { + p = new Parameters(); + Parameters.ParameterRef rs = p.addParameter(new TypeRef(ResultSet.class), "rs"); + g.add(new MethodRef(PUBLIC, entity.type, "fromResultSet", p, + "return Utils.fromResultSet" + entity.type.className + "(" + rs.name + ");"). + exception(sqlException)); + } + + { + p = new Parameters(); + Parameters.ParameterRef entity = p.addParameter(this.entity.type, "entity"); + g.add(new MethodRef(PUBLIC, VOID, "insert", p, + "Utils.insert" + this.entity.type.className + "(super.c, " + entity.name + ");"). + exception(sqlException)); + } + + { + p = new Parameters(); + Parameters.ParameterRef id = p.addParameter(entity.idType, "id"); + g.add(new MethodRef(PUBLIC, entity.type, "selectById", p, + "return Utils.select" + entity.type.className + "ById(super.c, " + id.name + ");"). + exception(sqlException)); + } + + { + p = new Parameters(); + Parameters.ParameterRef where = p.addParameter(stringType, "where"); + g.add(new MethodRef(PUBLIC, listOfEntityType, "selectWhere", p, + "return Utils.select" + entity.type.className + "Where(super.c, " + where.name + ");"). + exception(sqlException)); + } + + { + p = new Parameters(); + Parameters.ParameterRef entity = p.addParameter(this.entity.type, "entity"); + g.add(new MethodRef(PUBLIC, VOID, "delete", p, + "Utils.delete" + this.entity.type.className + "(super.c, " + entity.name + ");"). + exception(sqlException)); + } + + { + p = new Parameters(); + Parameters.ParameterRef id = p.addParameter(entity.idType, "id"); + g.add(new MethodRef(PUBLIC, VOID, "deleteById", p, + "Utils.delete" + entity.type.className + "ById(super.c, " + id.name + ");"). + exception(sqlException)); + } + + { + p = new Parameters(); + Parameters.ParameterRef entity = p.addParameter(this.entity.type, "entity"); + g.add(new MethodRef(PUBLIC, VOID, "update", p, + "Utils.update" + this.entity.type.className + "(super.c, " + entity.name + ");"). + exception(sqlException)); + } + + return g; + } + + public String createTableSql() { + List<String> columns = new ArrayList<>(); + for (FieldMirror field : entity.fields) { + String s; + if (field.fieldType == PRIMITIVE) { + 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.fieldType == REFERENCE) { + EntityMirror referenced = unit.get(field.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 " + entity.tableName + "(%n" + + join(columns, ",%n") + + "%n);"); + } + + public String dropTableSql() { + return "DROP TABLE " + entity.tableName + ";"; + } + + public String insertIntoSql() { + List<String> columns = new ArrayList<>(); + List<String> values = new ArrayList<>(); + for (FieldMirror field : entity.fields) { + columns.add(field.sqlName); + if (field.id) { + values.add("nextval('" + unit.getDefaultSequence().sequenceName + "')"); + } else { + values.add("?"); + } + } + + return "INSERT INTO " + entity.tableName + "(" + join(columns, ", ") + ") " + + "VALUES(" + join(values, ", ") + ");"; + } + + public String deleteFromSql() { + List<String> ss = new ArrayList<>(); + for (FieldMirror field : entity.idFields) { + ss.add(field.sqlName + "=?"); + } + + return "DELETE FROM " + entity.tableName + " WHERE " + join(ss, " AND ") + ";"; + } + + public String defaultFields() { + List<String> names = new ArrayList<>(); + for (FieldMirror field : entity.fields) { + names.add(field.sqlName); + } + + return join(names, ", "); + } +} diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoUtilsGenerator.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoUtilsGenerator.java new file mode 100644 index 0000000..115223a --- /dev/null +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoUtilsGenerator.java @@ -0,0 +1,265 @@ +package io.trygvis.persistence.generators; + +import io.trygvis.container.compiler.SqlUnitModel; +import io.trygvis.container.compiler.model.ClassG; +import io.trygvis.container.compiler.model.Constructor; +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.EntityMirror; +import io.trygvis.persistence.FieldMirror; +import io.trygvis.persistence.GeneratorConfiguration; +import io.trygvis.persistence.TypeHandler; +import io.trygvis.persistence.sql.AbstractTypedQuery; +import io.trygvis.persistence.sql.FromResultSet; + +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.Utils.toGetterName; +import static io.trygvis.container.compiler.Utils.toSetterName; +import static io.trygvis.persistence.FieldMirror.AccessorType.FIELD; +import static io.trygvis.persistence.FieldMirror.AccessorType.METHOD; +import static io.trygvis.persistence.FieldMirror.FieldType.PRIMITIVE; +import static io.trygvis.persistence.FieldMirror.FieldType.REFERENCE; +import static io.trygvis.persistence.generators.GeneratorUtils.staticVersion; +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 DaoUtilsGenerator { + + private final SqlUnitModel unit; + private final GeneratorConfiguration generatorConfiguration; + private final EntityMirror entity; + + public DaoUtilsGenerator(SqlUnitModel unit, GeneratorConfiguration generatorConfiguration, EntityMirror entity) { + this.unit = unit; + this.generatorConfiguration = generatorConfiguration; + this.entity = entity; + } + + public ClassG utils() { + ClassG g = new ClassG(PUBLIC | STATIC, entity.utilsType); + g.add(insertInto(g.imports)); + g.add(selectById(g.imports)); + g.add(selectWhere(g.imports)); + g.add(update(g.imports)); + g.add(delete(g.imports)); + g.add(deleteById(g.imports)); + g.add(query(g.imports)); + MethodRef fromResultSet = fromResultSet(g.imports); + g.add(fromResultSet); + g.add(staticVersion(new TypeRef(FromResultSet.class).args(entity.type), "fromResultSet", fromResultSet, g.type)); + return g; + } + + public MethodRef insertInto(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(); + Parameters.ParameterRef con = p.addParameter(conType, "con"); + Parameters.ParameterRef o = p.addParameter(entity.type, "o"); + + List<String> body = new ArrayList<>(); + + body.add("try(" + psType + " stmt = " + con.name + ".prepareStatement(insertIntoSql)) {"); + int i = 0; + for (FieldMirror field : entity.fields) { + // Assume all ID fields are generated for now. + if (field.id) { + continue; + } + + i++; + + TypeHandler typeHandler; + String accessor; + String setter; + if (field.fieldType == PRIMITIVE) { + typeHandler = generatorConfiguration.typeHandler(field.type); + accessor = field.fieldAccessor(o); + setter = " stmt." + typeHandler.setter(i, accessor) + ";"; + } else { + EntityMirror referenced = unit.get(field.type); + FieldMirror idField = referenced.getIdField(); + typeHandler = generatorConfiguration.typeHandler(idField.type); + accessor = field.referenceAccessor(o, idField); + setter = " stmt." + typeHandler.setter(i, accessor) + ";"; + accessor = "null"; + } + + if (field.notNull) { + body.add(setter); + } else { + body.add(" " + field.type + " " + field.javaName + " = " + accessor + ";"); + body.add(" if (" + field.javaName + " == null) {"); + body.add(" stmt.setNull(" + i + ", " + typesType + "." + typeHandler.typeName() + ");"); + body.add(" } else {"); + body.add(" " + setter); + body.add(" }"); + } + } + body.add(" stmt.executeUpdate();"); + body.add("}"); + return new MethodRef(PUBLIC | STATIC, TypeRef.VOID, "insert" + entity.type.className, p, body).exception(sqlExceptionType); + } + + public MethodRef selectById(Imports imports) { + Parameters p = new Parameters(); + p.addParameter(imports.add(Connection.class), "c"); + p.addParameter(entity.idType, "id"); + return new MethodRef(PUBLIC | STATIC, entity.type, "select" + entity.type.className + "ById", p, + "throw new UnsupportedOperationException(\"Not implemented\");"); + } + + public MethodRef selectWhere(Imports imports) { + TypeRef sqlExceptionType = imports.add(SQLException.class); + Parameters p = new Parameters(); + Parameters.ParameterRef c = p.addParameter(imports.add(Connection.class), "c"); + Parameters.ParameterRef where = p.addParameter(imports.add(String.class), "where"); + List<String> body = new ArrayList<>(); + body.add("String sql = \"SELECT \" + desc.defaultFields + \" FROM " + entity.tableName + " WHERE \" + " + where.name + " + \";\";"); + body.add("return runQuery(" + c.name + ", sql, " + entity.utilsType + ".fromResultSet" + entity.type.className + ");"); + + TypeRef listOfEntityType = new TypeRef(List.class).args(entity.type); + return new MethodRef(PUBLIC | STATIC, listOfEntityType, "select" + entity.type.className + "Where", p, body). + exception(sqlExceptionType); + } + + public MethodRef update(Imports imports) { + Parameters p = new Parameters(); + p.addParameter(imports.add(Connection.class), "c"); + p.addParameter(entity.type, "entity"); + return new MethodRef(PUBLIC | STATIC, entity.type, "update" + entity.type.className, p, + "throw new UnsupportedOperationException(\"Not implemented\");"); + } + + public MethodRef delete(Imports imports) { + TypeRef conType = imports.add(Connection.class); + TypeRef objectType = imports.add(entity.type); + Parameters p = new Parameters(); + Parameters.ParameterRef con = p.addParameter(conType, "con"); + Parameters.ParameterRef o = p.addParameter(objectType, "o"); + + List<String> arguments = new ArrayList<>(); + arguments.add(con.name); + for (FieldMirror field : entity.idFields) { + if (field.accessorType == FIELD) { + arguments.add(o.name + "." + field.javaName); + } else { + arguments.add(o.name + "." + toGetterName(field.javaName) + "()"); + } + } + + return new MethodRef(PUBLIC | STATIC, TypeRef.VOID, "delete" + entity.type.className, p, + "delete" + entity.type.className + "ById(" + join(arguments, ", ") + ");").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(); + Parameters.ParameterRef con = p.addParameter(conType, "con"); + + List<String> body = new ArrayList<>(); + + body.add("try(" + psType + " stmt = " + con.name + ".prepareStatement(deleteFromSql)) {"); + for (int i = 0; i < entity.idFields.size(); i++) { + FieldMirror field = entity.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, "delete" + entity.type.className + "ById", p, body). + exception(imports.add(SQLException.class)); + } + + public ClassG queryType(Imports imports) { + TypeRef sqlQueryType = imports.add(AbstractTypedQuery.class).args(entity.type); + TypeRef conType = imports.add(Connection.class); + TypeRef entityTypedQuery = new TypeRef(entity.type.className + "TypedQuery"); + TypeRef sqlExceptionType = new TypeRef(SQLException.class); + + Parameters p = new Parameters(); + Parameters.ParameterRef c = p.addParameter(conType, "c"); + Constructor constructor = new Constructor(p, singletonList("super(" + c.name + ", " + entity.daoType.className + ".desc);")); + ClassG g = new ClassG(PUBLIC | STATIC, entityTypedQuery). + extendsType(sqlQueryType). + add(constructor); + p = new Parameters(); + Parameters.ParameterRef rs = p.addParameter(new TypeRef(ResultSet.class), "rs"); + MethodRef fromResultSet = new MethodRef(PUBLIC, entity.type, "fromResultSet", p, + "return " + entity.utilsType.className + ".fromResultSet" + entity.type.className + "(" + rs.name + ");"). + exception(sqlExceptionType); + g.add(fromResultSet); + return g; + } + + public MethodRef query(Imports imports) { + TypeRef conType = imports.add(Connection.class); + TypeRef typedQueryType = imports.add(TypedQuery.class).args(entity.type); + TypeRef entityTypedQuery = new TypeRef(entity.type.className + "TypedQuery"); + + Parameters p = new Parameters(); + Parameters.ParameterRef c = p.addParameter(conType, "c"); + return new MethodRef(PUBLIC | STATIC, typedQueryType, "query" + entity.type.className, p, + "return new " + entityTypedQuery + "(" + c.name + ");"); + } + + public MethodRef fromResultSet(Imports g) { + TypeRef rsType = g.add(ResultSet.class); + Parameters p = new Parameters(); + Parameters.ParameterRef rs = p.addParameter(rsType, "rs"); + + List<String> body = new ArrayList<>(); + List<String> names = new ArrayList<>(); + for (int i = 0; i < entity.fields.size(); i++) { + FieldMirror field = entity.fields.get(i); + if (field.accessorType != FIELD) { + continue; + } + if (field.fieldType == PRIMITIVE) { + TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); + body.add(field.type + " " + field.javaName + " = " + typeHandler.getter(rs.name, i + 1) + ";"); + } else if (field.fieldType == REFERENCE) { + body.add(field.type + " " + field.javaName + " = null;"); + } + names.add(field.javaName); + } + + body.add(entity.type + " returnValue = new " + entity.type + "(" + join(names, ", ") + ");"); + + for (int i = 0; i < entity.fields.size(); i++) { + FieldMirror field = entity.fields.get(i); + if (field.accessorType != METHOD) { + continue; + } + if (field.fieldType == PRIMITIVE) { + TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); + body.add("returnValue." + toSetterName(field.javaName) + "(" + typeHandler.getter(rs.name, i + 1) + ");"); + } else if (field.fieldType == REFERENCE) { + body.add("returnValue." + toSetterName(field.javaName) + "(" + null + ");"); + } + names.add(field.javaName); + } + + body.add("return returnValue;"); + + return new MethodRef(PUBLIC | STATIC, entity.type, "fromResultSet" + entity.type.className, p, body). + exception(g.add(SQLException.class)); + } +} diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/GeneratorUtils.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/GeneratorUtils.java new file mode 100644 index 0000000..7dc083e --- /dev/null +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/GeneratorUtils.java @@ -0,0 +1,46 @@ +package io.trygvis.persistence.generators; + +import io.trygvis.container.compiler.model.FieldRef; +import io.trygvis.container.compiler.model.MethodRef; +import io.trygvis.container.compiler.model.Parameters; +import io.trygvis.container.compiler.model.TypeRef; + +import java.io.CharArrayWriter; +import java.io.PrintWriter; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.reflect.Modifier.*; +import static org.apache.commons.lang.StringUtils.join; + +public class GeneratorUtils { + public static FieldRef staticVersion(TypeRef interfaceType, String interfaceMethod, MethodRef methodRef, TypeRef klass) { + if (!Modifier.isStatic(methodRef.modifiers)) { + throw new RuntimeException("This only works on static methods."); + } + + List<String> params = new ArrayList<>(); + List<String> args = new ArrayList<>(); + for (Parameters.ParameterRef parameter : methodRef.parameters) { + params.add(parameter.klass.plainName + " " + parameter.name); + args.add(parameter.name); + } + + List<String> exceptions = new ArrayList<>(); + for (TypeRef exception : methodRef.exceptions) { + exceptions.add(exception.plainName); + } + + String e = exceptions.isEmpty() ? "" : "throws " + join(exceptions, ", ") + " "; + + CharArrayWriter buffer = new CharArrayWriter(); + PrintWriter w = new PrintWriter(buffer); + w.println("new " + interfaceType.plainName + "() {"); + w.println(" public " + methodRef.returnType.plainName + " " + interfaceMethod + "(" + join(params, ", ") + ") " + e + "{"); + w.println(" return " + klass.plainName + "." + methodRef.name + "(" + join(args, ", ") + ");"); + w.println(" }"); + w.println("}"); + return new FieldRef(PUBLIC | STATIC | FINAL, interfaceType, methodRef.name).value(buffer.toString()); + } +} diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionFactoryGenerator.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionFactoryGenerator.java new file mode 100644 index 0000000..3f0da2b --- /dev/null +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionFactoryGenerator.java @@ -0,0 +1,67 @@ +package io.trygvis.persistence.generators; + +import io.trygvis.container.compiler.SqlUnitModel; +import io.trygvis.container.compiler.model.ClassG; +import io.trygvis.container.compiler.model.Constructor; +import io.trygvis.container.compiler.model.FieldRef; +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.EntityMirror; +import io.trygvis.persistence.sql.SqlEntityMeta; +import io.trygvis.persistence.sql.SqlSessionFactory; +import io.trygvis.persistence.sql.SqlUnit; + +import javax.sql.DataSource; +import java.io.IOException; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.List; + +import static io.trygvis.container.compiler.Utils.toClassName; +import static java.lang.reflect.Modifier.*; +import static org.apache.commons.lang.StringUtils.join; + +public class SqlSessionFactoryGenerator { + public static ClassG generateSqlSessionFactory(SqlUnitModel unit) throws IOException { + String prefix = unit.getPackageName() + "." + toClassName(unit.getName()); + TypeRef ssfType = new TypeRef(prefix + "SqlSessionFactory"); + TypeRef ssType = new TypeRef(prefix + "SqlSession"); + + ClassG g = new ClassG(PUBLIC, ssfType). + extendsType(new TypeRef(SqlSessionFactory.class).args(ssType)); + + List<String> s = new ArrayList<>(); + for (EntityMirror entity : unit.getEntities().values()) { + s.add(entity.daoType.plainName + ".desc"); + } + TypeRef sqlEntityMetaArrayType = new TypeRef(SqlEntityMeta[].class); + FieldRef entities = g.addField(PUBLIC | STATIC, sqlEntityMetaArrayType, "entities"). + value("new " + sqlEntityMetaArrayType + "{" + join(s, ", ") + "}"); + + g.add(constructor(entities, g.imports)); + g.add(newSession(unit, ssType, g.imports)); + return g; + } + + private static Constructor constructor(FieldRef entities, Imports imports) { + TypeRef dataSourceType = imports.add(DataSource.class); + TypeRef sqlUnitType = imports.add(SqlUnit.class); + Parameters p = new Parameters(); + Parameters.ParameterRef ds = p.addParameter(dataSourceType, "ds"); + ArrayList<String> body = new ArrayList<>(); + body.add("super(new " + sqlUnitType.plainName + "(" + entities.name + "), " + ds.name + ");"); + return new Constructor(p, body); + } + + private static MethodRef newSession(SqlUnitModel unit, TypeRef sessionType, Imports imports) { + String prefix = unit.getPackageName() + "." + toClassName(unit.getName()); + TypeRef connectionType = imports.add(Connection.class); + + Parameters p = new Parameters(); + Parameters.ParameterRef c = p.addParameter(connectionType, "c"); + return new MethodRef(PROTECTED, sessionType, "newSession", p, + "return new " + sessionType + "(" + c.name + ");"); + } +} diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionGenerator.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionGenerator.java new file mode 100644 index 0000000..7a46018 --- /dev/null +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionGenerator.java @@ -0,0 +1,129 @@ +package io.trygvis.persistence.generators; + +import io.trygvis.container.compiler.SqlUnitModel; +import io.trygvis.container.compiler.model.AnnotationG; +import io.trygvis.container.compiler.model.ClassG; +import io.trygvis.container.compiler.model.Constructor; +import io.trygvis.container.compiler.model.FieldRef; +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.EntityMirror; +import io.trygvis.persistence.sql.SqlDao; +import io.trygvis.persistence.sql.SqlSession; + +import java.io.IOException; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static io.trygvis.container.compiler.Utils.toClassName; +import static io.trygvis.container.compiler.Utils.toFieldName; +import static io.trygvis.container.compiler.model.Parameters.ParameterRef; +import static java.lang.reflect.Modifier.PUBLIC; + +public class SqlSessionGenerator { + public static ClassG generateSqlSession(SqlUnitModel unit) throws IOException { + String prefix = unit.getPackageName() + "." + toClassName(unit.getName()); + TypeRef emType = new TypeRef(prefix + "SqlSession"); + TypeRef sqlSession = new TypeRef(SqlSession.class); + + ClassG g = new ClassG(PUBLIC, emType). + extendsType(sqlSession); + + Map<EntityMirror, FieldRef> daoFields = new HashMap<>(); + for (EntityMirror entity : unit.getEntities().values()) { + FieldRef f = g.addPublicFinalField(entity.daoType, toFieldName(entity.type.className)); + daoFields.put(entity, f); + } + + g.add(constructor(unit, g.imports, daoFields)); + g.add(getSqlDao(unit, g.imports, daoFields)); + + return g; + } + + private static Constructor constructor(SqlUnitModel unit, Imports imports, Map<EntityMirror, FieldRef> daoFields) { + Parameters p = new Parameters(); + ParameterRef c = p.addParameter(imports.add(Connection.class), "c"); + List<String> body = new ArrayList<>(); + body.add("super(" + c.name + ");"); + + for (EntityMirror entity : unit.getEntities().values()) { + FieldRef f = daoFields.get(entity); + body.add("this." + f.name + " = new " + entity.daoType.plainName + "(" + c.name + ");"); + } + return new Constructor(p, body); + } + + public static MethodRef getSqlDao(SqlUnitModel unit, Imports imports, Map<EntityMirror, FieldRef> daoFields) { + TypeRef sqlDatoType = imports.add(new TypeRef(SqlDao.class)).args("Id", "T"); + Parameters p = new Parameters(); + TypeRef klassType = new TypeRef(Class.class).args("T"); + ParameterRef klass = p.addParameter(klassType, "klass"); + List<String> body = new ArrayList<>(); + for (EntityMirror entity : unit.getEntities().values()) { + body.add("if (klass == " + entity.type.plainName + ".class) {"); + body.add(" return (SqlDao<Id, T>) " + daoFields.get(entity).name + ";"); + body.add("}"); + } + body.add("throw new RuntimeException(\"Type is not a part of this persistence unit: \" + klass);"); + return new MethodRef(PUBLIC, sqlDatoType, "getDao", p, body).typeArgs("Id", "T"). + annotation(new AnnotationG(new TypeRef(SuppressWarnings.class), "\"unchecked\"")); + } + + /* + public static ClassG sem(EntityMirror entity) { + ClassG g = new ClassG(PUBLIC, new TypeRef(entity.type.className + "SEM")); + g.implementsType(g.imports.add(new TypeRef(SqlDao.class)).args(entity.idType, entity.type)); + return g. + add(semFind(entity, g.imports)). + add(semPersist(entity, g.imports)). + add(semMerge(entity, g.imports)). + add(semRemove(entity, g.imports)); + } + + public static MethodRef semFind(EntityMirror entity, Imports imports) { + TypeRef type = imports.add(entity.type); + TypeRef sqlException = imports.add(SQLException.class); + Parameters p = new Parameters(); + ParameterRef primaryKey = p.addParameter(new TypeRef(Object.class), "primaryKey"); + List<String> body = new ArrayList<>(); + body.add("throw new " + sqlException.plainName + "(\"Not implemented\");"); + return new MethodRef(PUBLIC, type, "find", p, tryCatchSqlException(imports, body)); + } + + public static MethodRef semPersist(EntityMirror entity, Imports imports) { + TypeRef type = imports.add(entity.type); + TypeRef dao = imports.add(entity.daoType); + Parameters p = new Parameters(); + ParameterRef e = p.addParameter(type, "entity"); + List<String> body = new ArrayList<>(); + body.add(dao.plainName + ".insertInto(currentConnection(), " + e.name + ");"); + return new MethodRef(PUBLIC, VOID, "persist", p, tryCatchSqlException(imports, body)); + } + + public static MethodRef semMerge(EntityMirror entity, Imports imports) { + TypeRef type = imports.add(entity.type); + TypeRef sqlException = imports.add(SQLException.class); + Parameters p = new Parameters(); + ParameterRef e = p.addParameter(type, "entity"); + List<String> body = new ArrayList<>(); + body.add("throw new " + sqlException.plainName + "(\"Not implemented\");"); + return new MethodRef(PUBLIC, type, "merge", p, tryCatchSqlException(imports, body)); + } + + public static MethodRef semRemove(EntityMirror entity, Imports imports) { + TypeRef type = imports.add(entity.type); + TypeRef sqlException = imports.add(SQLException.class); + Parameters p = new Parameters(); + ParameterRef e = p.addParameter(type, "entity"); + List<String> body = new ArrayList<>(); + body.add("throw new " + sqlException.plainName + "(\"Not implemented\");"); + return new MethodRef(PUBLIC, VOID, "remove", p, tryCatchSqlException(imports, body)); + } + */ +} diff --git a/container-compiler-plugin/src/test/java/io/trygvis/persistence/EntityMirrorTest.java b/container-compiler-plugin/src/test/java/io/trygvis/persistence/EntityMirrorTest.java index 590b8d0..c513157 100644 --- a/container-compiler-plugin/src/test/java/io/trygvis/persistence/EntityMirrorTest.java +++ b/container-compiler-plugin/src/test/java/io/trygvis/persistence/EntityMirrorTest.java @@ -4,6 +4,8 @@ import io.trygvis.container.compiler.SqlUnitModel; import io.trygvis.container.compiler.model.Imports; import io.trygvis.container.compiler.model.MethodRef; import io.trygvis.container.compiler.model.TypeRef; +import io.trygvis.persistence.generators.DaoGenerator; +import io.trygvis.persistence.generators.DaoUtilsGenerator; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @@ -20,6 +22,8 @@ import static org.testng.Assert.assertEquals; public class EntityMirrorTest { + GeneratorConfiguration generatorConfiguration = new GeneratorConfiguration(); + static TypeRef entityType = new TypeRef("Wat"); static FieldMirror idLong = new FieldMirror(PRIMITIVE, FIELD, new TypeRef(Long.class), "id", "id", true, false, true); static FieldMirror idString = new FieldMirror(PRIMITIVE, FIELD, new TypeRef(String.class), "id", "id", true, false, true); @@ -73,10 +77,11 @@ public class EntityMirrorTest { } SqlUnitModel unit = new SqlUnitModel().add(myTable).add(new SequenceMirror("seq-gen", "id_seq", 0, 0)); + DaoGenerator generator = new DaoGenerator(generatorConfiguration, unit, myTable); - assertEquals(myTable.insertIntoSql(unit), insert); - assertEquals(myTable.deleteFromSql(), delete); - assertEquals(myTable.createTableSql(unit), create); + assertEquals(generator.insertIntoSql(), insert); + assertEquals(generator.deleteFromSql(), delete); + assertEquals(generator.createTableSql(), create); } @DataProvider(name = "insertIntoMethod", parallel = true) @@ -115,10 +120,10 @@ public class EntityMirrorTest { } private MethodRef insertInto(FieldMirror... fields) { - EntityMirror myTable = new EntityMirror(new GeneratorConfiguration(), entityType, "my_table"); + EntityMirror myTable = new EntityMirror(generatorConfiguration, entityType, "my_table"); myTable.add(fields); SqlUnitModel unit = new SqlUnitModel().add(myTable); - return myTable.insertInto(unit, new Imports()); + return new DaoUtilsGenerator(unit, generatorConfiguration, myTable).insertInto(new Imports()); } @DataProvider(name = "deleteMethod", parallel = true) @@ -141,7 +146,8 @@ public class EntityMirrorTest { private MethodRef delete(FieldMirror... fields) { EntityMirror myTable = new EntityMirror(new GeneratorConfiguration(), entityType, "my_table"); myTable.add(fields); - return myTable.delete(new Imports()); + SqlUnitModel unit = new SqlUnitModel().add(myTable); + return new DaoUtilsGenerator(unit, generatorConfiguration, myTable).delete(new Imports()); } @DataProvider(name = "deleteByIdMethod", parallel = true) @@ -170,7 +176,8 @@ public class EntityMirrorTest { private MethodRef deleteById(FieldMirror... fields) { EntityMirror myTable = new EntityMirror(new GeneratorConfiguration(), entityType, "my_table"); myTable.add(fields); - return myTable.deleteById(new Imports()); + SqlUnitModel unit = new SqlUnitModel().add(myTable); + return new DaoUtilsGenerator(unit, generatorConfiguration, myTable).deleteById(new Imports()); } @Test @@ -197,7 +204,8 @@ public class EntityMirrorTest { private MethodRef fromResultSet(FieldMirror... fields) { EntityMirror myTable = new EntityMirror(new GeneratorConfiguration(), entityType, "my_table"); myTable.add(fields); - return myTable.fromResultSet(new Imports()); + SqlUnitModel unit = new SqlUnitModel().add(myTable); + return new DaoUtilsGenerator(unit, generatorConfiguration, myTable).fromResultSet(new Imports()); } private void eq(MethodRef m, String... expected) { diff --git a/myapp/src/main/java/io/trygvis/container/myapp/AddressBookDirect.java b/myapp/src/main/java/io/trygvis/container/myapp/AddressBookDirect.java index df0f283..86e8641 100644 --- a/myapp/src/main/java/io/trygvis/container/myapp/AddressBookDirect.java +++ b/myapp/src/main/java/io/trygvis/container/myapp/AddressBookDirect.java @@ -1,16 +1,17 @@ package io.trygvis.container.myapp; -import javax.persistence.TypedQuery; +import io.trygvis.persistence.sql.SqlExecutor; + import java.io.BufferedReader; import java.io.EOFException; import java.io.InputStreamReader; import java.sql.Connection; -import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; +import java.util.ArrayList; import java.util.List; -import static io.trygvis.container.myapp.CompanyDao.Utils.insertCompany; import static io.trygvis.container.myapp.Contact.Gender.FEMALE; import static io.trygvis.container.myapp.Contact.Gender.MALE; @@ -18,10 +19,14 @@ public class AddressBookDirect { private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); - private Connection c; + private MyAppSqlSession session; + + private static MyAppSqlSessionFactory sessionFactory; public static void main(String[] args) throws Exception { try { + String jdbcUrl = "jdbc:h2:mem:address-book;DB_CLOSE_DELAY=-1"; + sessionFactory = new MyAppSqlSessionFactory(new DriverManagerDataSource(jdbcUrl)); new AddressBookDirect().work(); } catch (EOFException ignore) { } @@ -30,14 +35,13 @@ public class AddressBookDirect { private void work() throws Exception { boolean done = false; while (!done) { - c = DriverManager.getConnection("jdbc:h2:mem:address-book;DB_CLOSE_DELAY=-1"); try { - c.setAutoCommit(false); + session = sessionFactory.newSession(); done = main(); - c.commit(); + session.commit(); System.out.println("OK"); } finally { - c.close(); + session.close(); } } } @@ -91,21 +95,19 @@ public class AddressBookDirect { } public void create() throws SQLException { - Statement statement = c.createStatement(); - statement.executeUpdate(CompanyDao.createTableSql); - statement.executeUpdate(ContactDao.createTableSql); + session.executeUpdate(CompanyDao.createTableSql); + session.executeUpdate(ContactDao.createTableSql); for (String sql : Sequences.createSequences) { - statement.executeUpdate(sql); + session.executeUpdate(sql); } } public void drop() throws SQLException { - Statement statement = c.createStatement(); for (String sql : Sequences.dropSequences) { - statement.executeUpdate(sql); + session.executeUpdate(sql); } - statement.executeUpdate(ContactDao.dropTableSql); - statement.executeUpdate(CompanyDao.dropTableSql); + session.executeUpdate(ContactDao.dropTableSql); + session.executeUpdate(CompanyDao.dropTableSql); } // ----------------------------------------------------------------------- @@ -115,13 +117,13 @@ public class AddressBookDirect { private void company() throws Exception { while (true) { System.out.println("Company menu:"); - System.out.println("c Create"); - System.out.println("d Drop"); - System.out.println("d List"); + System.out.println("a Add"); + System.out.println("d Delete"); + System.out.println("l List"); System.out.println("q Back"); String cmd = cmd(); switch (cmd) { - case "c": + case "a": addCompany(); break; case "d": @@ -143,16 +145,14 @@ public class AddressBookDirect { String name = line(); Company company = new Company(name); - insertCompany(c, company); + session.company.insert(company); } public void deleteCompany() { } - public void listCompanies() { - TypedQuery<Company> p = CompanyDao.Utils.queryCompany(c); - - List<Company> resultList = p.getResultList(); + public void listCompanies() throws SQLException { + List<Company> resultList = session.company.selectWhere("1=1"); for (Company company : resultList) { System.out.println("====================="); System.out.println("Id: " + company.getId()); @@ -168,12 +168,13 @@ public class AddressBookDirect { private void contact() throws Exception { while (true) { System.out.println("Contact menu:"); - System.out.println("c Create"); - System.out.println("d Drop"); + System.out.println("a Add"); + System.out.println("d Delete"); + System.out.println("l list"); System.out.println("q Back"); String cmd = cmd(); switch (cmd) { - case "c": + case "a": addContact(); break; case "d": @@ -215,18 +216,16 @@ public class AddressBookDirect { Company company = null; Contact o = new Contact(name, g, company); - ContactDao.Utils.insertContact(c, o); + session.contact.insert(o); } public void deleteContact() { } - public void listContacts() { - TypedQuery<Contact> p = ContactDao.Utils.queryContact(c); - - List<Contact> resultList = p.getResultList(); - for (Contact contact : resultList) { + public void listContacts() throws SQLException { + List<Contact> contacts = session.contact.selectWhere("1=1"); + for (Contact contact : contacts) { System.out.println("====================="); System.out.println("Id: " + contact.getId()); System.out.println("Name: " + contact.name); diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java index 7df3658..2c82474 100644 --- a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java +++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java @@ -1,7 +1,11 @@ package io.trygvis.persistence.sql; import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; public abstract class SqlDao<Id, T> implements FromResultSet<T> { @@ -17,9 +21,26 @@ public abstract class SqlDao<Id, T> implements FromResultSet<T> { public abstract void deleteById(Id id) throws SQLException; -// public abstract TypedQuery<T> query(); - public abstract T selectById(Id id) throws SQLException; public abstract void update(T entity) throws SQLException; + + public abstract List<T> selectWhere(String where) throws SQLException; + + // ----------------------------------------------------------------------- + // Protected + // ----------------------------------------------------------------------- + + protected static <T> List<T> runQuery(Connection c, String sql, FromResultSet<T> f) throws SQLException { + try (PreparedStatement stmt = c.prepareStatement(sql)) { + ResultSet rs = stmt.executeQuery(); + + List<T> list = new ArrayList<>(); + while (rs.next()) { + list.add(f.fromResultSet(rs)); + } + + return list; + } + } } diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSession.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSession.java new file mode 100644 index 0000000..3caa621 --- /dev/null +++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSession.java @@ -0,0 +1,31 @@ +package io.trygvis.persistence.sql; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.List; + +public class SqlSession { + private final Connection c; + + public SqlSession(Connection c) { + this.c = c; + } + + public void commit() throws SQLException { + c.commit(); + } + + public void close() throws SQLException { + c.close(); + } + + public void executeUpdate(String sql) throws SQLException { + Statement stmt = c.createStatement(); + stmt.executeUpdate(sql); + } + + public <T> List<T> query(SqlExecutor.QueryCommand<T> query) throws SQLException { + return query.run(c); + } +} diff --git a/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSessionFactory.java b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSessionFactory.java new file mode 100644 index 0000000..adf36d3 --- /dev/null +++ b/sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSessionFactory.java @@ -0,0 +1,23 @@ +package io.trygvis.persistence.sql; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +public abstract class SqlSessionFactory<T extends SqlSession> { + private final SqlUnit unit; + private final DataSource ds; + + public SqlSessionFactory(SqlUnit unit, DataSource ds) { + this.unit = unit; + this.ds = ds; + } + + public T newSession() throws SQLException { + Connection c = ds.getConnection(); + + return newSession(c); + } + + protected abstract T newSession(Connection c); +} |