Created
October 11, 2015 12:07
-
-
Save orekyuu/2f72749a408affb41342 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app; | |
import app.entity.User; | |
import lib.SqlWrapper; | |
import java.util.List; | |
public class App { | |
private static final String url = "jdbc:derby://localhost:1527/test"; | |
public static void main(String[] args) { | |
SqlWrapper wrapper = new SqlWrapper(url); | |
UserRepository userRepository = wrapper.createRepository(UserRepository.class); | |
List<User> all = userRepository.findAll(); | |
for (User user : all) { | |
System.out.println(user); | |
} | |
System.out.println("findByAge"); | |
for (User user : userRepository.findUsersByAge(20)) { | |
System.out.println(user); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app.entity; | |
import lib.annotations.Column; | |
import lib.annotations.Table; | |
@Table("USER_TABLE") | |
public class User { | |
@Column("ID") | |
private long id; | |
@Column("USER_NAME") | |
private String name; | |
@Column("AGE") | |
private long age; | |
public long getId() { | |
return id; | |
} | |
public String getName() { | |
return name; | |
} | |
public long getAge() { | |
return age; | |
} | |
@Override | |
public String toString() { | |
final StringBuilder sb = new StringBuilder("User{"); | |
sb.append("id=").append(id); | |
sb.append(", name='").append(name).append('\''); | |
sb.append(", age=").append(age); | |
sb.append('}'); | |
return sb.toString(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app; | |
import app.entity.User; | |
import lib.Repository; | |
import lib.annotations.Parameter; | |
import lib.annotations.Select; | |
import java.util.List; | |
import java.util.Optional; | |
public interface UserRepository extends Repository<User> { | |
@Select("WHERE USER_NAME = ':name'") | |
Optional<User> findByName(@Parameter("name") String name); | |
@Select("WHERE AGE = :age") | |
List<User> findUsersByAge(@Parameter("age") int age); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.FIELD) | |
public @interface Column { | |
/** | |
* @return カラムの名前 | |
*/ | |
String value(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.METHOD) | |
public @interface Delete { | |
String value() default ""; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.PARAMETER) | |
public @interface Parameter { | |
String value(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.METHOD) | |
public @interface Select { | |
String value() default ""; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.annotations; | |
import java.lang.annotation.ElementType; | |
import java.lang.annotation.Retention; | |
import java.lang.annotation.RetentionPolicy; | |
import java.lang.annotation.Target; | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(ElementType.TYPE) | |
public @interface Table { | |
/** | |
* @return テーブルの名前 | |
*/ | |
String value(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.processor; | |
import java.sql.ResultSet; | |
import java.sql.SQLException; | |
import java.util.List; | |
public interface ResultSetFunction { | |
List<Object> accept(ResultSet resultSet) throws SQLException; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.processor; | |
import lib.annotations.Column; | |
import lib.annotations.Select; | |
import lib.annotations.Table; | |
import java.lang.reflect.*; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Objects; | |
import java.util.Optional; | |
public class SelectMethodProcessor extends SqlProcessorBase { | |
private final Select select; | |
public SelectMethodProcessor(String jdbcUrl, Type type, Select select) { | |
super(jdbcUrl, type); | |
this.select = select; | |
} | |
@Override | |
protected Object doProcess(Object proxy, Method method, Object[] args) { | |
String sql = createSql(method, args); | |
List<Object> result = getResult(sql); | |
//戻り値の型に合わせる | |
Class<?> returnType = method.getReturnType(); | |
if (returnType.isAssignableFrom(Optional.class)) { | |
return result.isEmpty() ? Optional.empty() : Optional.ofNullable(result.get(0)); | |
} | |
if (returnType.isAssignableFrom(List.class)) { | |
return result; | |
} | |
return result.isEmpty() ? null : result.get(0); | |
} | |
private String createSql(Method method, Object[] args) { | |
Table table = getEntityClass().getAnnotation(Table.class); | |
Objects.requireNonNull(table, "Tableアノテーションが見つかりません"); | |
StringBuilder sqlBuilder = new StringBuilder("SELECT * FROM " + table.value()); | |
String sql = select.value(); | |
Parameter[] parameters = method.getParameters(); | |
for (int i = 0; i < parameters.length; i++) { | |
lib.annotations.Parameter annotation = parameters[i].getAnnotation(lib.annotations.Parameter.class); | |
Objects.requireNonNull(annotation, "Parameterアノテーションが見つかりません"); | |
sql = sql.replace(":" + annotation.value(), args[i].toString()); | |
} | |
return sqlBuilder.append(" ").append(sql).toString(); | |
} | |
private List<Object> getResult(String sql) { | |
return executeQuery(sql, resultSet -> { | |
Class<?> entityClass = getEntityClass(); | |
ArrayList<Object> list = new ArrayList<>(); | |
while (resultSet.next()) { | |
try { | |
//結果を入れるEntityを作成 | |
Object entityInstance = entityClass.getConstructor().newInstance(); | |
list.add(entityInstance); | |
//結果をフィールドに詰める | |
for (Field field : entityClass.getDeclaredFields()) { | |
Column column = field.getAnnotation(Column.class); | |
if (column == null) { | |
continue; | |
} | |
int index = resultSet.findColumn(column.value()); | |
Object object = resultSet.getObject(index); | |
field.setAccessible(true); | |
//フィールドがOptionalならOptionalに入れる | |
if (field.getType().isAssignableFrom(Optional.class)) { | |
field.set(entityInstance, Optional.ofNullable(object)); | |
} else { | |
field.set(entityInstance, object); | |
} | |
} | |
} catch (ReflectiveOperationException e) { | |
e.printStackTrace(); | |
} | |
} | |
return list; | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.processor; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Type; | |
import java.sql.*; | |
import java.util.ArrayList; | |
import java.util.List; | |
public abstract class SqlProcessorBase { | |
private Class<?> entityClass; | |
private final String jdbcUrl; | |
public SqlProcessorBase(String jdbcUrl, Type type) { | |
this.jdbcUrl = jdbcUrl; | |
try { | |
this.entityClass = Class.forName(type.getTypeName()); | |
} catch (ClassNotFoundException e) { | |
e.printStackTrace(); | |
} | |
} | |
public Class<?> getEntityClass() { | |
return entityClass; | |
} | |
public final Object process(Object proxy, Method method, Object[] args) { | |
return doProcess(proxy, method, args); | |
} | |
protected abstract Object doProcess(Object proxy, Method method, Object[] args); | |
protected final List<Object> executeQuery(String sql, ResultSetFunction consumer) { | |
System.out.println("executeQuery: " + sql); | |
try (Connection connection = DriverManager.getConnection(jdbcUrl); | |
Statement statement = connection.createStatement()) { | |
ResultSet resultSet = statement.executeQuery(sql); | |
return consumer.accept(resultSet); | |
} catch (SQLException e) { | |
e.printStackTrace(); | |
} | |
return new ArrayList<>(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib.processor; | |
public class ValidateException extends RuntimeException { | |
public ValidateException(String message) { | |
super(message); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib; | |
import lib.annotations.Select; | |
import java.util.List; | |
public interface Repository<ENTITY> { | |
@Select | |
List<ENTITY> findAll(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib; | |
import java.lang.reflect.Proxy; | |
import java.util.Objects; | |
public class SqlWrapper { | |
private final String jdbcUrl; | |
public SqlWrapper(String jdbcUrl) { | |
Objects.requireNonNull(jdbcUrl); | |
this.jdbcUrl = jdbcUrl; | |
} | |
@SuppressWarnings("unchecked") | |
public <T extends Repository> T createRepository(Class<T> repositoryInterface) { | |
return (T) Proxy.newProxyInstance( | |
repositoryInterface.getClassLoader(), | |
new Class[]{repositoryInterface}, | |
new SqlWrapperInvocationHandler(jdbcUrl, repositoryInterface)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package lib; | |
import lib.annotations.Select; | |
import lib.processor.SelectMethodProcessor; | |
import lib.processor.SqlProcessorBase; | |
import lib.processor.ValidateException; | |
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.ParameterizedType; | |
import java.lang.reflect.Type; | |
import java.util.Objects; | |
class SqlWrapperInvocationHandler implements InvocationHandler { | |
private final String jdbcUrl; | |
private final Class proxyInterface; | |
private Type entityType; | |
SqlWrapperInvocationHandler(String jdbcUrl, Class proxyInterface) { | |
this.jdbcUrl = jdbcUrl; | |
this.proxyInterface = proxyInterface; | |
getEntityType(proxyInterface); | |
if (entityType instanceof ParameterizedType) { | |
ParameterizedType type = (ParameterizedType) this.entityType; | |
entityType = type.getActualTypeArguments()[0]; | |
} | |
Objects.requireNonNull(entityType, "Entityが見つけられませんでした"); | |
} | |
@SuppressWarnings("unchecked") | |
private Class<Repository> getEntityType(Class<?> clazz) { | |
for (Class<?> a : clazz.getInterfaces()) { | |
if (a == Repository.class) { | |
entityType = clazz.getGenericInterfaces()[0]; | |
return (Class<Repository>) a; | |
} else { | |
Class<Repository> repositoryClass = getEntityType(a); | |
if (repositoryClass != null) { | |
entityType = a.getGenericInterfaces()[0]; | |
return repositoryClass; | |
} | |
} | |
} | |
return null; | |
} | |
@Override | |
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { | |
SqlProcessorBase processor = null; | |
Select select = method.getAnnotation(Select.class); | |
if (select != null) { | |
processor = new SelectMethodProcessor(jdbcUrl, entityType, select); | |
} | |
if (processor == null) { | |
throw new ValidateException(proxy.getClass().getName() + "#" + method.getName() + "に対応するProcessorが見つかりません。"); | |
} | |
return processor.process(proxy, method, args); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment