mirror of
https://github.com/russ-p/external-sql.git
synced 2025-12-13 17:04: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