mirror of
https://github.com/russ-p/external-sql.git
synced 2025-12-14 01:14:24 +00:00
add src
This commit is contained in:
52
pom.xml
Normal file
52
pom.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<groupId>com.github.russ-p</groupId>
|
||||||
|
<artifactId>external-sql</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
<version>0.1-SNAPSHOT</version>
|
||||||
|
<name>external-sql</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<java.version>1.8</java.version>
|
||||||
|
<bytebuddy.version>1.9.10</bytebuddy.version>
|
||||||
|
<assertj.version>3.11.1</assertj.version>
|
||||||
|
<junit.version>4.12</junit.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.bytebuddy</groupId>
|
||||||
|
<artifactId>byte-buddy</artifactId>
|
||||||
|
<version>${bytebuddy.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>${junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.assertj</groupId>
|
||||||
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>${assertj.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>${java.version}</source>
|
||||||
|
<target>${java.version}</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
||||||
17
src/main/java/com/github/russ_p/externalsql/ExternalSQL.java
Normal file
17
src/main/java/com/github/russ_p/externalsql/ExternalSQL.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package com.github.russ_p.externalsql;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(value = ElementType.TYPE)
|
||||||
|
@Retention(value = RetentionPolicy.RUNTIME)
|
||||||
|
public @interface ExternalSQL {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* file with sql queries
|
||||||
|
*/
|
||||||
|
String value();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.github.russ_p.externalsql;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import net.bytebuddy.ByteBuddy;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType.Builder.MethodDefinition.ImplementationDefinition.Optional;
|
||||||
|
import net.bytebuddy.dynamic.DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition;
|
||||||
|
import net.bytebuddy.implementation.FixedValue;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
|
public class ExternalSQLFactory {
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T create(Class<T> clazz) throws IOException, InstantiationException, IllegalAccessException {
|
||||||
|
ExternalSQL anno = clazz.getAnnotation(ExternalSQL.class);
|
||||||
|
Objects.requireNonNull(anno, "Class must be annotated with @ExternalSQL");
|
||||||
|
|
||||||
|
String srcFile = anno.value();
|
||||||
|
SqlResolver resolver = new SqlResolver(srcFile);
|
||||||
|
|
||||||
|
Optional<Object> implement = new ByteBuddy()
|
||||||
|
.subclass(Object.class)
|
||||||
|
.implement(clazz);
|
||||||
|
|
||||||
|
ReceiverTypeDefinition<Object> intercept = implement
|
||||||
|
.method(ElementMatchers.named("toString"))
|
||||||
|
.intercept(FixedValue.value("SQL queries from " + srcFile));
|
||||||
|
|
||||||
|
for (String name : resolver.getQueryNames()) {
|
||||||
|
intercept = intercept
|
||||||
|
.method(ElementMatchers.named(name))
|
||||||
|
.intercept(FixedValue.value(resolver.getQuery(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Class<?> dynamicType = intercept
|
||||||
|
.make()
|
||||||
|
.load(getClass().getClassLoader())
|
||||||
|
.getLoaded();
|
||||||
|
|
||||||
|
return (T) dynamicType.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
src/main/java/com/github/russ_p/externalsql/SqlResolver.java
Normal file
94
src/main/java/com/github/russ_p/externalsql/SqlResolver.java
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package com.github.russ_p.externalsql;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
class SqlResolver {
|
||||||
|
|
||||||
|
private static final String NAME_COMMENT_PREFIX = "-- @";
|
||||||
|
private static final String COMMENT_PREFIX = "--";
|
||||||
|
|
||||||
|
private final Map<String, String> queries = new HashMap<>();
|
||||||
|
|
||||||
|
public SqlResolver(String filename) throws IOException {
|
||||||
|
this(new File(resolvePath(filename)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SqlResolver(File file) throws IOException {
|
||||||
|
parseFile(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getQueryNames() {
|
||||||
|
return queries.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQuery(String name) {
|
||||||
|
return queries.getOrDefault(name, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void put(String name, String query) {
|
||||||
|
if (name == null || name.isEmpty())
|
||||||
|
return;
|
||||||
|
if (query == null || query.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
queries.put(name, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseFile(File file) throws IOException {
|
||||||
|
String queryName = "";
|
||||||
|
StringBuffer query = new StringBuffer();
|
||||||
|
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
|
||||||
|
String line = reader.readLine();
|
||||||
|
while (line != null) {
|
||||||
|
if (isSqlHeaderComment(line)) {
|
||||||
|
put(queryName, query.toString().trim());
|
||||||
|
queryName = getName(line);
|
||||||
|
query = new StringBuffer();
|
||||||
|
} else if (isSimpleComment(line)) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
query.append(line).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// read next line
|
||||||
|
line = reader.readLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
put(queryName, query.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSimpleComment(String line) {
|
||||||
|
return line.startsWith(COMMENT_PREFIX) && !isSqlHeaderComment(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isSqlHeaderComment(String line) {
|
||||||
|
return line.startsWith(NAME_COMMENT_PREFIX);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getName(String line) {
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(line, " ");
|
||||||
|
while (tokenizer.hasMoreElements()) {
|
||||||
|
String str = tokenizer.nextToken();
|
||||||
|
if (str.startsWith("@")) {
|
||||||
|
return str.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("No sql query name found in line " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String resolvePath(String path) {
|
||||||
|
if (path.startsWith("classpath:")) {
|
||||||
|
return SqlResolver.class.getClassLoader().getResource(path.substring(10)).getFile();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.github.russ_p.externalsql;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ExternalSQLFactoryTest {
|
||||||
|
|
||||||
|
private ExternalSQLFactory factory;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
factory = new ExternalSQLFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreate() throws Exception {
|
||||||
|
TestQueries testQueries = factory.create(TestQueries.class);
|
||||||
|
|
||||||
|
assertThat(testQueries.selectOne()).isNotEmpty();
|
||||||
|
assertThat(testQueries.selectOne()).isEqualTo("select * from test_table;");
|
||||||
|
assertThat(testQueries.selectTwo()).isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.github.russ_p.externalsql;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SqlResolverTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSqlResolverString() throws Exception {
|
||||||
|
SqlResolver resolver = new SqlResolver(
|
||||||
|
SqlResolverTest.class.getClassLoader().getResource("test.sql").getFile());
|
||||||
|
|
||||||
|
assertThat(resolver.getQueryNames()).hasSize(2);
|
||||||
|
|
||||||
|
assertThat(resolver.getQuery("selectOne")).isNotEmpty();
|
||||||
|
assertThat(resolver.getQuery("selectOne")).isEqualTo("select * from test_table;");
|
||||||
|
|
||||||
|
assertThat(resolver.getQuery("selectTwo")).isNotEmpty();
|
||||||
|
assertThat(resolver.getQuery("notExist")).isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSqlResolverClasspathString() throws Exception {
|
||||||
|
SqlResolver resolver = new SqlResolver("classpath:test.sql");
|
||||||
|
|
||||||
|
assertThat(resolver).isNotNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.github.russ_p.externalsql;
|
||||||
|
|
||||||
|
@ExternalSQL("classpath:test.sql")
|
||||||
|
public interface TestQueries {
|
||||||
|
|
||||||
|
String selectOne();
|
||||||
|
|
||||||
|
String selectTwo();
|
||||||
|
}
|
||||||
17
src/test/resources/test.sql
Normal file
17
src/test/resources/test.sql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-- Ext-SQL test file
|
||||||
|
|
||||||
|
-- @selectOne
|
||||||
|
select * from test_table;
|
||||||
|
|
||||||
|
-- @selectTwo
|
||||||
|
-- comment example
|
||||||
|
SELECT
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
|
||||||
|
FROM test_table
|
||||||
|
|
||||||
|
WHERE
|
||||||
|
a > 1;
|
||||||
|
|
||||||
|
-- end of file
|
||||||
Reference in New Issue
Block a user