From 6d4c6960e69b53c124bd84beb3d008bd5a4bb319 Mon Sep 17 00:00:00 2001 From: Trygve Laugstøl Date: Sun, 4 Aug 2013 18:50:18 +0200 Subject: wip o Adding Joda time's DateTime and UUID. o Registering @SequenceGenerator's on fields and getters. o Skipping static methods. o Generating SQL to drop sequences. --- .../trygvis/container/compiler/EntityHandler.java | 133 +++++++++++++++------ .../io/trygvis/container/compiler/MyProcessor.java | 4 +- .../trygvis/container/compiler/SqlUnitModel.java | 8 +- .../java/io/trygvis/persistence/EntityMirror.java | 11 +- .../persistence/GeneratorConfiguration.java | 2 + .../io/trygvis/persistence/SequenceMirror.java | 30 ++++- .../java/io/trygvis/persistence/TypeHandler.java | 32 +++++ .../trygvis/container/compiler/ProcessorTest.java | 3 +- .../io/trygvis/persistence/EntityMirrorTest.java | 11 +- .../io/trygvis/persistence/test/ChildEntity.java | 18 +++ .../io/trygvis/persistence/test/ParentEntity.java | 6 +- .../io/trygvis/container/myapp/AddressBook.java | 5 +- 12 files changed, 210 insertions(+), 53 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 98090a4..53811d9 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 @@ -16,6 +16,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; @@ -27,6 +28,7 @@ import javax.tools.JavaFileObject; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -43,6 +45,7 @@ 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.capitalize; import static org.apache.commons.lang.StringUtils.join; import static org.apache.commons.lang.StringUtils.stripEnd; @@ -87,7 +90,7 @@ public class EntityHandler extends AbstractHandler { } public void recordEntity(TypeElement element) throws Exception { - EntityMirror entityMirror = new EntityMirror( + EntityMirror entity = new EntityMirror( generatorConfiguration, new TypeRef(types.getDeclaredType(element)), sqlName(element.getSimpleName().toString())); @@ -125,7 +128,7 @@ public class EntityHandler extends AbstractHandler { if (field == null) { continue; } - entityMirror.add(field); + entity.add(field); System.out.println("Field: " + field); } @@ -137,44 +140,34 @@ public class EntityHandler extends AbstractHandler { if (field == null) { continue; } - entityMirror.add(field); + entity.add(field); System.out.println("Field: " + field); } + Iterator it = setters.values().iterator(); + while (it.hasNext()) { + ExecutableElement setter = it.next(); + if (setter.getModifiers().contains(Modifier.STATIC)) { + it.remove(); + } + } + if (!setters.isEmpty()) { - throw new CompilerException(element, "Missing getters for setters: " + join(setters.keySet(), ", ")); + throw new CompilerException(element, "Missing getters for these setters: " + join(setters.keySet(), ", ")); } // ----------------------------------------------------------------------- // Process any extra annotations // ----------------------------------------------------------------------- - AnnotationMirror sequenceGenerator = findAnnotation(SequenceGenerator.class, element); - - if (sequenceGenerator != null) { - String name = null; - for (Map.Entry v : sequenceGenerator.getElementValues().entrySet()) { - String field = v.getKey().getSimpleName().toString(); - switch (field) { - case "name": - name = v.getValue().getValue().toString(); - - break; - default: - throw new InternalErrorException("Unsupported field on @SequenceGenerator: " + field); - } - } - if (name != null) { - sqlUnit.add(new SequenceMirror(name)); - } - } + processSequenceGenerators(element); // ----------------------------------------------------------------------- // Validation // ----------------------------------------------------------------------- List idFields = new ArrayList<>(); - for (FieldMirror field : entityMirror.fields) { + for (FieldMirror field : entity.fields) { if (field.id) { idFields.add(field); } @@ -187,7 +180,42 @@ public class EntityHandler extends AbstractHandler { throw new CompilerException(element, "This implementation only support a single @Id annotated field."); } - sqlUnit.add(entityMirror, element); + sqlUnit.add(entity, element); + } + + private void processSequenceGenerators(Element element) { + AnnotationMirror sequenceGenerator = findAnnotation(SequenceGenerator.class, element); + + if (sequenceGenerator != null) { + String name = null; + String sequenceName = null; + int initialValue = 0; + int allocationSize = 0; + + for (Map.Entry v : sequenceGenerator.getElementValues().entrySet()) { + String field = v.getKey().getSimpleName().toString(); + switch (field) { + case "name": + name = v.getValue().getValue().toString(); + break; + case "sequenceName": + sequenceName = v.getValue().getValue().toString(); + break; + case "initialValue": + initialValue = Integer.valueOf(v.getValue().getValue().toString()); + break; + case "allocationSize": + allocationSize = Integer.valueOf(v.getValue().getValue().toString()); + break; + default: + throw new InternalErrorException("Unsupported field on @SequenceGenerator: " + field); + } + } + if (name != null) { + sequenceName = sequenceName == null ? sqlName(name) : sequenceName; + sqlUnit.add(new SequenceMirror(name, sequenceName, initialValue, allocationSize), element); + } + } } public ClassG phase3(EntityMirror entityMirror) throws IOException { @@ -230,14 +258,27 @@ public class EntityHandler extends AbstractHandler { boolean id; if (var != null) { + if (var.getModifiers().contains(Modifier.STATIC)) { + return null; + } accessorType = FIELD; type = new TypeRef(var.asType()); javaName = var.getSimpleName().toString(); id = isId(var); + processSequenceGenerators(var); } else { + if (getter.getModifiers().contains(Modifier.STATIC)) { + return null; + } + if (setter == null) { + // Skipping fields is closer to what hibernate does. + return null; +// throw new CompilerException(getter, "Missing setter for getter: " + getter.getSimpleName()); + } accessorType = METHOD; type = new TypeRef(getter.getReturnType()); id = isId(getter); + processSequenceGenerators(getter); // TODO: this might be relaxed, just find the common type and use that. if (!types.isSameType(getter.getReturnType(), setter.getParameters().get(0).asType())) { throw new CompilerException(format("The setter and getter %s/%s must access the same types.", @@ -269,26 +310,44 @@ public class EntityHandler extends AbstractHandler { return var.getAnnotation(Id.class) != null; } - public void phase3() throws Exception { - for (EntityMirror entity : sqlUnit.getEntities().values()) { - writeFile(phase3(entity), sqlUnit.element(entity)); + public void phase3(boolean errorRaised) throws Exception { + System.out.println("errorRaised = " + errorRaised); + try { + for (EntityMirror entity : sqlUnit.getEntities().values()) { + writeFile(phase3(entity), sqlUnit.element(entity)); + } + writeFile(generateSequences(sqlUnit), null); + writeFile(generateSession(), null); + } catch (CompilerException | InternalErrorException e) { + // Ignore any exceptions if we had an error from before + if (errorRaised) { + return; + } + throw e; } - writeFile(generateSequences(sqlUnit), null); - writeFile(generateSession(), null); } private ClassG generateSequences(SqlUnitModel unit) { TypeRef sequences = new TypeRef(unit.getPackageName() + ".Sequences"); ClassG g = new ClassG(PUBLIC, sequences); - List fields = new ArrayList<>(); + List creates = new ArrayList<>(); + List drops = new ArrayList<>(); for (SequenceMirror sequence : unit.getSequences().values()) { TypeRef stringType = g.imports.add(String.class); - String value = "CREATE SEQUENCE " + sequence.name + ";"; - FieldRef f = g.addPublicStaticFinalField(stringType, sequence.name).value(toJavaString(value)); - fields.add(f.name); + String value = "CREATE SEQUENCE " + sequence.sequenceName + ";"; + FieldRef f = g.addPublicStaticFinalField(stringType, "create" + capitalize(sequence.name)). + value(toJavaString(value)); + creates.add(f.name); + + f = g.addPublicStaticFinalField(stringType, "drop" + capitalize(sequence.name)). + value(toJavaString("DROP SEQUENCE " + sequence.sequenceName) + ";"); + drops.add(f.name); + } - g.addPublicStaticFinalField(new TypeRef(String[].class), "sequences"). - value("new String[]{" + join(fields, ", ") + "}"); + g.addPublicStaticFinalField(new TypeRef(String[].class), "createSequences"). + value("new String[]{" + join(creates, ", ") + "}"); + g.addPublicStaticFinalField(new TypeRef(String[].class), "dropSequences"). + value("new String[]{" + join(drops, ", ") + "}"); return g; } @@ -336,7 +395,7 @@ public class EntityHandler extends AbstractHandler { return builder.toString(); } - private AnnotationMirror findAnnotation(Class c, TypeElement type) { + private AnnotationMirror findAnnotation(Class c, Element type) { TypeMirror annotationType = elements.getTypeElement(c.getCanonicalName()).asType(); for (AnnotationMirror a : type.getAnnotationMirrors()) { if (types.isSameType(a.getAnnotationType(), annotationType)) { 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 89da439..fbbc56d 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 @@ -110,6 +110,7 @@ public class MyProcessor implements Processor { Set packages = ElementFilter.packagesIn(roundEnv.getElementsAnnotatedWith(SqlEntitySet.class)); entityHandler.phase1(sqlEntities, packages); + boolean hadErrors = false; for (Element element : roundEnv.getRootElements()) { System.out.println("Processing: " + element.asType()); for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { @@ -128,12 +129,13 @@ public class MyProcessor implements Processor { // e.printStackTrace(System.out); Messager messager = processingEnv.getMessager(); messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage(), e.element); + hadErrors = true; } } } } - entityHandler.phase3(); + entityHandler.phase3(hadErrors); return true; } diff --git a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/SqlUnitModel.java b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/SqlUnitModel.java index 568bbe7..1bd2535 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/SqlUnitModel.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/container/compiler/SqlUnitModel.java @@ -67,13 +67,17 @@ public class SqlUnitModel { // ----------------------------------------------------------------------- public SqlUnitModel add(SequenceMirror sequence, Element element) { + Element e = sequenceElements.get(sequence); + if (e != null && !e.equals(element)) { + throw new CompilerException(element, "This unit already contains a sequence called '" + sequence.name + "'."); + } sequences.put(sequence.name, sequence); sequenceElements.put(sequence, element); return this; } - public SqlUnitModel add(SequenceMirror... sequenceMirrors) { - for (SequenceMirror sequenceMirror : sequenceMirrors) { + public SqlUnitModel add(SequenceMirror... sequences) { + for (SequenceMirror sequenceMirror : sequences) { this.sequences.put(sequenceMirror.name, sequenceMirror); } 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 f1970f7..f0f4ae7 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 @@ -111,7 +111,7 @@ public class EntityMirror implements Comparable { for (FieldMirror field : fields) { columns.add(field.sqlName); if (field.id) { - values.add("nextval('" + unit.getDefaultSequence().name + "')"); + values.add("nextval('" + unit.getDefaultSequence().sequenceName + "')"); } else { values.add("?"); } @@ -162,7 +162,12 @@ public class EntityMirror implements Comparable { if (field.fieldType == PRIMITIVE) { TypeHandler typeHandler = generatorConfiguration.typeHandler(field.type); - String access = o.name + "." + field.javaName; + String access; + if (field.accessorType == FIELD) { + access = o.name + "." + field.javaName; + } else { + access = o.name + "." + toGetterName(field.javaName) + "()"; + } String setter = " stmt." + typeHandler.setter(i, access) + ";"; if (field.notNull) { @@ -283,7 +288,6 @@ public class EntityMirror implements Comparable { body.add(field.type + " " + field.javaName + " = null;"); } names.add(field.javaName); - i++; } body.add(type + " returnValue = new " + type + "(" + join(names, ", ") + ");"); @@ -305,7 +309,6 @@ public class EntityMirror implements Comparable { body.add("returnValue." + toSetterName(field.javaName) + "(" + null + ");"); } names.add(field.javaName); - i++; } body.add("return returnValue;"); diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/GeneratorConfiguration.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/GeneratorConfiguration.java index 37d8146..9df6c35 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/persistence/GeneratorConfiguration.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/GeneratorConfiguration.java @@ -17,6 +17,8 @@ public class GeneratorConfiguration { typeHandlers.put(new TypeRef(Long.class), new TypeHandler.LongTypeHandler()); typeHandlers.put(new TypeRef(String.class), new TypeHandler.StringTypeHandler()); typeHandlers.put(new TypeRef(Date.class), new TypeHandler.DateTypeHandler()); + typeHandlers.put(new TypeRef("org.joda.time.DateTime"), new TypeHandler.JodaDateTimeTypeHandler()); + typeHandlers.put(new TypeRef("java.util.UUID"), new TypeHandler.UuidTypeHandler()); primitiveTypeHandlers.putAll(typeHandlers); } diff --git a/container-compiler-plugin/src/main/java/io/trygvis/persistence/SequenceMirror.java b/container-compiler-plugin/src/main/java/io/trygvis/persistence/SequenceMirror.java index 9ae6781..6a9da6c 100644 --- a/container-compiler-plugin/src/main/java/io/trygvis/persistence/SequenceMirror.java +++ b/container-compiler-plugin/src/main/java/io/trygvis/persistence/SequenceMirror.java @@ -1,9 +1,35 @@ package io.trygvis.persistence; -public class SequenceMirror { +public class SequenceMirror implements Comparable { public final String name; + public final String sequenceName; + public final int initialValue; + public final int allocationSize; - public SequenceMirror(String name) { + public SequenceMirror(String name, String sequenceName, int initialValue, int allocationSize) { this.name = name; + this.sequenceName = sequenceName; + this.initialValue = initialValue == 0 ? 10000 : initialValue; + this.allocationSize = allocationSize == 0 ? 1000 : allocationSize; + } + + @Override + public int compareTo(@SuppressWarnings("NullableProblems") SequenceMirror o) { + return name.compareTo(o.name); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SequenceMirror)) return false; + + SequenceMirror that = (SequenceMirror) o; + + return name.equals(that.name); + } + + @Override + public int hashCode() { + return name.hashCode(); } } 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 72941f2..31eba4b 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 @@ -116,4 +116,36 @@ public abstract class TypeHandler { return "new java.util.Date(" + rs + ".getTimestamp(" + i + ").getTime())"; } } + + public static class JodaDateTimeTypeHandler extends TypeHandler { + protected JodaDateTimeTypeHandler() { + super("TIMESTAMP", TIMESTAMP); + } + + @Override + public String setter(int i, String expr) { + return "setTimestamp(" + i + ", new java.sql.Timestamp(" + expr + ".getTime()))"; + } + + @Override + public String getter(String rs, int i) { + return "new org.joda.time.DateTime(" + rs + ".getTimestamp(" + i + ").getTime())"; + } + } + + public static class UuidTypeHandler extends TypeHandler { + protected UuidTypeHandler() { + super("CHAR(36)", CHAR); + } + + @Override + public String setter(int i, String expr) { + return "setString(" + i + ", " + expr + ".toString())"; + } + + @Override + public String getter(String rs, int i) { + return "java.util.UUID.fromString(" + rs + ".getString(" + i + "))"; + } + } } diff --git a/container-compiler-plugin/src/test/java/io/trygvis/container/compiler/ProcessorTest.java b/container-compiler-plugin/src/test/java/io/trygvis/container/compiler/ProcessorTest.java index c7891fc..f30b731 100644 --- a/container-compiler-plugin/src/test/java/io/trygvis/container/compiler/ProcessorTest.java +++ b/container-compiler-plugin/src/test/java/io/trygvis/container/compiler/ProcessorTest.java @@ -62,7 +62,8 @@ public class ProcessorTest { assertThat(fileManager.codes.keySet()).containsOnly( "io.trygvis.persistence.test.Sequences", "io.trygvis.persistence.test.Session", - "io.trygvis.persistence.test.PersonDao"); + "io.trygvis.persistence.test.PersonDao", + "io.trygvis.persistence.test.ChildEntityDao"); assertThat(collector.getDiagnostics()).isEmpty(); assertThat(result).isTrue(); 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 52b33ca..85e3741 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 @@ -71,7 +71,7 @@ public class EntityMirrorTest { myTable.add(field); } - SqlUnitModel unit = new SqlUnitModel().add(myTable).add(new SequenceMirror("id_seq")); + SqlUnitModel unit = new SqlUnitModel().add(myTable).add(new SequenceMirror("seq-gen", "id_seq", 0, 0)); assertEquals(myTable.insertIntoSql(unit), insert); assertEquals(myTable.deleteFromSql(), delete); @@ -92,9 +92,16 @@ public class EntityMirrorTest { " stmt.executeUpdate();", "}") }, - new Object[]{new FieldMirror[]{age}, join( + new Object[]{new FieldMirror[]{year}, join( + "try(java.sql.PreparedStatement stmt = con.prepareStatement(insertIntoSql)) {", + " stmt.setInt(1, o.getYear());", + " stmt.executeUpdate();", + "}") + }, + new Object[]{new FieldMirror[]{age, year, idLong}, join( "try(java.sql.PreparedStatement stmt = con.prepareStatement(insertIntoSql)) {", " stmt.setInt(1, o.age);", + " stmt.setInt(2, o.getYear());", " stmt.executeUpdate();", "}") }, diff --git a/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ChildEntity.java b/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ChildEntity.java index d886fca..a341fd2 100644 --- a/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ChildEntity.java +++ b/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ChildEntity.java @@ -10,4 +10,22 @@ public class ChildEntity extends ParentEntity { super(id); this.name = name; } + + // ----------------------------------------------------------------------- + // These static versions shouldn't affect anything + // ----------------------------------------------------------------------- + + public static void setSetterOnly(int x) { + } + + public static int getAccessorPair() { + return 0; + } + + public static void setAccessorPair(int x) { + } + + public static int getGetterOnly() { + return 0; + } } diff --git a/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ParentEntity.java b/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ParentEntity.java index 4695739..9925f7e 100644 --- a/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ParentEntity.java +++ b/container-compiler-plugin/src/test/resources/io/trygvis/persistence/test/ParentEntity.java @@ -4,14 +4,14 @@ import javax.persistence.Id; public class ParentEntity { @Id - public final Long id; + public Long id; + + private Integer age; public ParentEntity(Long id) { this.id = id; } - private Integer age; - public Integer getAge() { return age; } diff --git a/myapp/src/main/java/io/trygvis/container/myapp/AddressBook.java b/myapp/src/main/java/io/trygvis/container/myapp/AddressBook.java index 0101004..d5a1170 100644 --- a/myapp/src/main/java/io/trygvis/container/myapp/AddressBook.java +++ b/myapp/src/main/java/io/trygvis/container/myapp/AddressBook.java @@ -87,7 +87,7 @@ public class AddressBook { public void run(Connection c) throws Exception { Statement statement = c.createStatement(); statement.executeUpdate(ContactDao.createTableSql); - for (String sql : Sequences.sequences) { + for (String sql : Sequences.createSequences) { statement.executeUpdate(sql); } } @@ -98,6 +98,9 @@ public class AddressBook { public void run(Connection c) throws Exception { Statement statement = c.createStatement(); statement.executeUpdate(ContactDao.dropTableSql); + for (String sql : Sequences.dropSequences) { + statement.executeUpdate(sql); + } } } -- cgit v1.2.3