summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/EntityHandler.java103
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/MyProcessor.java8
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/container/compiler/model/ClassG.java5
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/persistence/EntityMirror.java309
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/persistence/TypeHandler.java8
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoGenerator.java214
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/DaoUtilsGenerator.java265
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/GeneratorUtils.java46
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionFactoryGenerator.java67
-rw-r--r--container-compiler-plugin/src/main/java/io/trygvis/persistence/generators/SqlSessionGenerator.java129
-rw-r--r--container-compiler-plugin/src/test/java/io/trygvis/persistence/EntityMirrorTest.java24
-rw-r--r--myapp/src/main/java/io/trygvis/container/myapp/AddressBookDirect.java69
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlDao.java25
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSession.java31
-rw-r--r--sql-persistence/src/main/java/io/trygvis/persistence/sql/SqlSessionFactory.java23
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);
+}