diff --git a/common/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapper.java b/common/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapper.java index 83074b5..e89de3e 100644 --- a/common/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapper.java +++ b/common/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapper.java @@ -48,6 +48,13 @@ public interface JdbcMapper extends Closeable { DatabaseType databaseType() default DatabaseType.DEFAULT; String arrayNumberTypeName() default ""; String arrayStringTypeName() default ""; + + /** + * Allows consumer of JdbcMapper.Mapper to generate DaoBean with Spring Boot Stereotype @Repository + * Defaults to false + * @return + */ + OptionalBool generateAsSpringBean() default OptionalBool.FALSE; } @Retention(RetentionPolicy.SOURCE) diff --git a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java index 34f58c2..25c86be 100644 --- a/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java +++ b/jdbcmapper/src/main/java/com/moparisthebest/jdbc/codegen/JdbcMapperProcessor.java @@ -10,6 +10,7 @@ import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.Diagnostic; import java.io.*; +import java.net.URL; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; @@ -30,7 +31,7 @@ import static com.moparisthebest.jdbc.codegen.JdbcMapperFactory.SUFFIX; * Created by mopar on 5/24/17. */ @SupportedAnnotationTypes("com.moparisthebest.jdbc.codegen.JdbcMapper.Mapper") -@SupportedOptions({"jdbcMapper.databaseType", "jdbcMapper.arrayNumberTypeName", "jdbcMapper.arrayStringTypeName", "jdbcMapper.allowedMaxRowParamNames", "jdbcMapper.sqlCheckerClass"}) +@SupportedOptions({"jdbcMapper.databaseType", "jdbcMapper.arrayNumberTypeName", "jdbcMapper.arrayStringTypeName", "jdbcMapper.allowedMaxRowParamNames", "jdbcMapper.sqlCheckerClass", "jdbcMapper.generateAsSpringBean"}) public class JdbcMapperProcessor extends AbstractProcessor { public static final Pattern paramPattern = Pattern.compile("\\{(([^\\s]+)\\s+(([Nn][Oo][Tt]\\s+)?[Ii][Nn]\\s+))?([BbCc][Ll][Oo][Bb]\\s*:\\s*([^:}]+\\s*:\\s*)?)?([^}]+)\\}"); @@ -171,6 +172,8 @@ public class JdbcMapperProcessor extends AbstractProcessor { final JdbcMapper.Mapper mapper = genClass.getAnnotation(JdbcMapper.Mapper.class); final JdbcMapper.DatabaseType databaseType; final String arrayNumberTypeName, arrayStringTypeName; + final boolean generateAsSpringBean = mapper.generateAsSpringBean().combine(false); + if (mapper.databaseType() == JdbcMapper.DatabaseType.DEFAULT) { databaseType = defaultDatabaseType; arrayNumberTypeName = !mapper.arrayNumberTypeName().isEmpty() ? mapper.arrayNumberTypeName() : defaultArrayNumberTypeName; @@ -223,10 +226,16 @@ public class JdbcMapperProcessor extends AbstractProcessor { w.write(packageName); w.write(";\n\n"); } + if (generateAsSpringBean) { + w.write("import org.springframework.stereotype.Repository;\n\n"); + } w.write("import com.moparisthebest.jdbc.Factory;\n\n"); w.write("import java.sql.*;\n\n"); w.write("import static com.moparisthebest.jdbc.util.ResultSetUtil.*;\n"); w.write("import static com.moparisthebest.jdbc.TryClose.tryClose;\n\n"); + if (generateAsSpringBean) { + w.write("@Repository\n"); + } w.write("public class "); w.write(className); if (isInterface) { @@ -1105,4 +1114,13 @@ public class JdbcMapperProcessor extends AbstractProcessor { } return false; } + + private final boolean isSpringRepositoryStereotypeRequired(final String stereoType) { + + if (Boolean.parseBoolean(processingEnv.getOptions().get("jdbcMapper.generateAsSpringBean"))) { + return processingEnv.getOptions().get("jdbcMapper.springBeanStereotype").equalsIgnoreCase(stereoType); + } + + return false; + } } diff --git a/readme.md b/readme.md index a605990..7f704d7 100644 --- a/readme.md +++ b/readme.md @@ -411,6 +411,49 @@ String s = rs.getString(index); return s == null ? null : ZoneOffset.of(s); ``` +Spring Boot Support +---- +Configuring your `JdbcMapperProcessor` generated `DAOBean` to be compatible with Spring's [ComponentScan](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html) can be +accomplished by simply setting the `JdbcMapper.Mapper.generateAsSpringBean` value to +`JdbcMapper.OptionalBool.TRUE`. This value is set to `JdbcMapper.OptionalBool.FALSE` by default. +```java +@JdbcMapper.Mapper(generateAsSpringBean = JdbcMapper.OptionalBool.TRUE) +public interface MyDAO extends JdbcMapper { + // Your method signatures and queries here +} +``` + +This configuration will generate a DAOBean looking something like this: +```java +package com.yourpackage; + +import org.springframework.stereotype.Repository; + +import com.moparisthebest.jdbc.Factory; + +import java.sql.*; + +import static com.moparisthebest.jdbc.util.ResultSetUtil.*; +import static com.moparisthebest.jdbc.TryClose.tryClose; + +@Repository +public class MyDAOBean implements MyDAO { + // Generated code here +} +``` + +Your DAOBean can now be [Autowired](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Autowired.html) +in a Spring Boot application: +```java +private final MyDAO myDAO; + +@Autowired +public MyController(MyDAO myDAO) { + this.myDAO = myDAO; +} + +``` + TODO ---- diff --git a/test/pom.xml b/test/pom.xml index a81d3f7..af98f8d 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -80,6 +80,11 @@ test true + + org.springframework + spring-context + 5.0.9.RELEASE + diff --git a/test/src/main/java/com/moparisthebest/jdbc/codegen/AbstractSpringBeanDAO.java b/test/src/main/java/com/moparisthebest/jdbc/codegen/AbstractSpringBeanDAO.java new file mode 100644 index 0000000..552f85a --- /dev/null +++ b/test/src/main/java/com/moparisthebest/jdbc/codegen/AbstractSpringBeanDAO.java @@ -0,0 +1,9 @@ +package com.moparisthebest.jdbc.codegen; + +@JdbcMapper.Mapper(jndiName = "bob", + databaseType = JdbcMapper.DatabaseType.ANY, + cachePreparedStatements = JdbcMapper.OptionalBool.FALSE, + allowReflection = JdbcMapper.OptionalBool.TRUE, + generateAsSpringBean = JdbcMapper.OptionalBool.TRUE) +public abstract class AbstractSpringBeanDAO implements JdbcMapper { +} diff --git a/test/src/main/java/com/moparisthebest/jdbc/codegen/SpringBeanDAO.java b/test/src/main/java/com/moparisthebest/jdbc/codegen/SpringBeanDAO.java new file mode 100644 index 0000000..62e7b1e --- /dev/null +++ b/test/src/main/java/com/moparisthebest/jdbc/codegen/SpringBeanDAO.java @@ -0,0 +1,9 @@ +package com.moparisthebest.jdbc.codegen; + +@JdbcMapper.Mapper(jndiName = "bob", + databaseType = JdbcMapper.DatabaseType.ANY, + cachePreparedStatements = JdbcMapper.OptionalBool.FALSE, + allowReflection = JdbcMapper.OptionalBool.TRUE, + generateAsSpringBean = JdbcMapper.OptionalBool.TRUE) +public interface SpringBeanDAO extends JdbcMapper { +} diff --git a/test/src/test/java/com/moparisthebest/jdbc/codegen/SpringBeanDAOTest.java b/test/src/test/java/com/moparisthebest/jdbc/codegen/SpringBeanDAOTest.java new file mode 100644 index 0000000..cbc3a70 --- /dev/null +++ b/test/src/test/java/com/moparisthebest/jdbc/codegen/SpringBeanDAOTest.java @@ -0,0 +1,59 @@ +package com.moparisthebest.jdbc.codegen; + +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; + +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertFalse; + +public class SpringBeanDAOTest { + + @Test + public void testSpringBeanDAOBeanImportsRepository() { + try { + String genClass = new String(Files.readAllBytes(Paths.get("target/generated-sources/annotations/com/moparisthebest/jdbc/codegen/SpringBeanDAOBean.java")), StandardCharsets.UTF_8); + assertTrue(genClass.contains("import org.springframework.stereotype.Repository;")); + } catch (IOException e) { + assertFalse("Failed to read file at target/generated-sources/annotations/com/moparisthebest/jdbc/codegen/SpringBeanDAOBean.java", true); + } + } + + @Test + public void testSpringBeanDAOHasRepositoryStereotype() { + try { + Class clazz = ClassLoader.getSystemClassLoader() + .loadClass("com.moparisthebest.jdbc.codegen.SpringBeanDAOBean"); + assertTrue(clazz.isAnnotationPresent(org.springframework.stereotype.Repository.class)); + } catch (ClassNotFoundException e) { + assertFalse("Failed to find class com.moparisthebest.jdbc.codegen.SpringBeanDAOBean", true); + } + } + + @Test + public void testSpringBeanDAOBeanImplementsSpringBeanDAO() { + try { + Class[] clazzInterfaces = ClassLoader.getSystemClassLoader() + .loadClass("com.moparisthebest.jdbc.codegen.SpringBeanDAOBean").getInterfaces(); + assertTrue(Arrays.asList(clazzInterfaces).contains(SpringBeanDAO.class)); + } catch (ClassNotFoundException e) { + assertFalse("Failed to find class com.moparisthebest.jdbc.codegen.SpringBeanDAOBean", true); + } + } + + + @Test + public void testAbstractSpringBeanDAOBeanExtendsAbstractSpringBeanDAO() { + try { + Class clazz = ClassLoader.getSystemClassLoader() + .loadClass("com.moparisthebest.jdbc.codegen.AbstractSpringBeanDAOBean"); + assertTrue(clazz.getSuperclass().equals(AbstractSpringBeanDAO.class)); + } catch (ClassNotFoundException e) { + assertFalse("Failed to find class com.moparisthebest.jdbc.codegen.AbstractSpringBeanDAOBean", true); + } + } +}