Redkale 技术详解 04 -- DataSource简易的DB操作
长期以来,Hibernate和Mybatis一直是大家使用最多的持久层开发框架。针对这两种框架网络上是各种比较,各种讨论优缺点。其实这两个框架(算上前身ibatis)都是2002年左右发布, 迄今已有15载已笨重不堪,一个mybatis.jar包大小6M左右,Hibernate更是巨大,毫无轻巧灵活可言,提供简化SQL操作的同时带来了复杂繁琐的配置和高学习门槛。而Redkale的Source组件非常轻量级,通过十多个interface、enum和十多个class完成常见的DB操作功能。
Source组件在接口设计上参考了JPA接口,为了降低学习成本,部分注解仍沿用javax.persistence中的类,以多个注解结合一个主操作类 DataSource 的方式提供API。在没有IDE自动生成代码的插件支持的情况下Redkale提供了一个Demo代码 AutoClassCreator 能很方便的将数据库表生成Entity类。
。
Source组件在接口设计上参考了JPA接口,为了降低学习成本,部分注解仍沿用javax.persistence中的类,以多个注解结合一个主操作类 DataSource 的方式提供API。在没有IDE自动生成代码的插件支持的情况下Redkale提供了一个Demo代码 AutoClassCreator 能很方便的将数据库表生成Entity类。
Constructor<T> constructor0 = null;
for (Constructor c : clazz.getConstructors()) { //优先找public 的构造函数
if (c.getParameterCount() == 0) {
constructor0 = c;
break;
}
}
if (constructor0 == null) {//其次找非private带ConstructorProperties的构造函数
for (Constructor c : clazz.getDeclaredConstructors()) {
if (Modifier.isPrivate(c.getModifiers())) continue;
if (c.getAnnotation(ConstructorProperties.class) != null) {
constructor0 = c;
break;
}
}
}
if (constructor0 == null) {//再次找非private且带-parameters编译项的构造函数 java 8以上才支持
for (Constructor c : clazz.getDeclaredConstructors()) {
if (Modifier.isPrivate(c.getModifiers())) continue;
Parameter[] params = c.getParameters();
if (params.length == 0) continue;
boolean flag = true;
for (Parameter param : params) {
try {
clazz.getDeclaredField(param.getName());
} catch (Exception e) {
flag = false;
break;
}
}
if (flag) {
constructor0 = c;
break;
}
}
}
if (constructor0 == null) {//最后找非private的空构造函数
for (Constructor c : clazz.getDeclaredConstructors()) {
if (Modifier.isPrivate(c.getModifiers())) continue;
if (c.getParameterCount() == 0) {
constructor0 = c;
break;
}
}
}从以上代码可以看出,根据优先级选择Constructor,为了减少学习成本,Creator直接重用了java.beans.ConstructorProperties注解,又因ConstructorProperties只能标记在Constructor上,因此定义一个Creator.ConstructorParameters注解,用于标记在Creator的create方法上。
public class Record {
private final int id;
private String name;
@ConstructorProperties({"id", "name"})
Record(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Record.class通过ASM自动构建与Record同package的Creator类如下:
public final class Record_DynCreator implements Creator<Record> {
@Override
@Creator.ConstructorParameters({"id", "name"})
public Record create(Object... params) {
if (params[0] == null) params[0] = 0;
return new Record((Integer) params[0], (String) params[1]);
}
}如上代码,若构造参数是primitive类,而Creator.create传入的参数可能是null,因此需要给null的primitive对象赋予默认值0。细心的人可能发现了Record的构造函数并不是public的,虽然Record_DynCreator与Record在同一package,但由于两者不是同一个ClassLoader,故不能直接new Record。Redkale曲线救国,通过URLClassLoader的私有方法在Record.class的ClassLoader加载Record_DynCreator。
if (loader instanceof URLClassLoader && !Modifier.isPublic(constructor.getModifiers())) {
try {
final URLClassLoader urlLoader = (URLClassLoader) loader;
final URL url = new URL("memclass", "localhost", -1, "/" + newDynName.replace('/', '.') + "/", new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new URLConnection(u) {
@Override
public void connect() throws IOException {
}
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(bytes);
}
};
}
});
Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addURLMethod.setAccessible(true);
addURLMethod.invoke(urlLoader, url);
resultClazz = urlLoader.loadClass(newDynName.replace('/', '.'));
} catch (Throwable t) { //异常无需理会, 使用下一种loader方式
t.printStackTrace();
}
}如上代码,构建一个虚拟协议的URL来实现加载,若Record.class的ClassLoader不是URLClassLoader导致resultClazz为null则会抛出异常。
Creator是一个典型通过ASM构建一个简单功能地动态类,同类型还有 org.redkale.util.Attribute、org.redkale.util.Reproduce。