sql
This commit is contained in:
@@ -13,7 +13,7 @@ import org.redkale.convert.json.JsonConvert;
|
||||
* 存放方法的字节信息
|
||||
*
|
||||
* @see org.redkale.asm.AsmMethodBoost
|
||||
*
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
public class AsmMethodBean {
|
||||
@@ -65,6 +65,17 @@ public class AsmMethodBean {
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> fieldNameList() {
|
||||
if (params == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<String> rs = new ArrayList<>(params.size());
|
||||
for (AsmMethodParam p : params) {
|
||||
rs.add(p.getName());
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
public String[] fieldNameArray() {
|
||||
if (params == null) {
|
||||
return null;
|
||||
|
||||
33
src/main/java/org/redkale/persistence/Sql.java
Normal file
33
src/main/java/org/redkale/persistence/Sql.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
*
|
||||
*/
|
||||
package org.redkale.persistence;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 原始sql语句
|
||||
*
|
||||
*
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @see org.redkale.source.DataSqlMapper
|
||||
*
|
||||
* @author zhangjx
|
||||
*
|
||||
* @since 2.8.0
|
||||
*/
|
||||
@Inherited
|
||||
@Documented
|
||||
@Target({ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Sql {
|
||||
|
||||
String value();
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import org.redkale.annotation.*;
|
||||
import org.redkale.annotation.AutoLoad;
|
||||
import org.redkale.annotation.ResourceType;
|
||||
import static org.redkale.boot.Application.*;
|
||||
import org.redkale.convert.ConvertDisabled;
|
||||
import org.redkale.inject.ResourceEvent;
|
||||
import org.redkale.net.AsyncGroup;
|
||||
import org.redkale.persistence.Table;
|
||||
@@ -654,6 +655,11 @@ public abstract class AbstractDataSqlSource extends AbstractDataSource implement
|
||||
return nativeSqlParser.parse(signFunc, dbtype(), nativeSql, params == null ? Collections.emptyMap() : params);
|
||||
}
|
||||
|
||||
@ConvertDisabled
|
||||
public IntFunction<String> getSignFunc() {
|
||||
return signFunc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(AnyValue config) {
|
||||
super.destroy(config);
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.redkale.util.Sheet;
|
||||
* <p>
|
||||
* 详情见: https://redkale.org
|
||||
*
|
||||
* @see org.redkale.persistence.Sql
|
||||
*
|
||||
* @author zhangjx
|
||||
* @param <T> T
|
||||
@@ -3309,97 +3310,4 @@ public interface DataSqlMapper<T> {
|
||||
return dataSource().querySheetAsync(entityType(), selects, flipper, node);
|
||||
}
|
||||
|
||||
//----------------------- native ----------------------------
|
||||
/**
|
||||
* 执行多条原生无参数的sql
|
||||
*
|
||||
* @param sqls 无参数的sql语句
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default int[] nativeUpdates(String... sqls) {
|
||||
return dataSource().nativeUpdates(sqls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行多条原生无参数的sql
|
||||
*
|
||||
* @param sqls 无参数的sql语句
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default CompletableFuture<int[]> nativeUpdatesAsync(String... sqls) {
|
||||
return dataSource().nativeUpdatesAsync(sqls);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原生无参数的sql
|
||||
*
|
||||
* @param sql 无参数的sql语句
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default int nativeUpdate(String sql) {
|
||||
return dataSource().nativeUpdate(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原生无参数的sql
|
||||
*
|
||||
* @param sql 无参数的sql语句
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default CompletableFuture<Integer> nativeUpdateAsync(String sql) {
|
||||
return dataSource().nativeUpdateAsync(sql);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原生带参数的sql
|
||||
*
|
||||
* @param sql 带参数的sql语句
|
||||
* @param params 参数值集合
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default int nativeUpdate(String sql, Map<String, Object> params) {
|
||||
return dataSource().nativeUpdate(sql, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原生带参数的sql
|
||||
*
|
||||
* @param sql 带参数的sql语句
|
||||
* @param params 参数值集合
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default CompletableFuture<Integer> nativeUpdateAsync(String sql, Map<String, Object> params) {
|
||||
return dataSource().nativeUpdateAsync(sql, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原生带参数的sql
|
||||
*
|
||||
* @param sql 带参数的sql语句
|
||||
* @param bean 参数值集合
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default int nativeUpdate(String sql, Serializable bean) {
|
||||
return dataSource().nativeUpdate(sql, bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行原生带参数的sql
|
||||
*
|
||||
* @param sql 带参数的sql语句
|
||||
* @param bean 参数值集合
|
||||
*
|
||||
* @return 执行条数
|
||||
*/
|
||||
default CompletableFuture<Integer> nativeUpdateAsync(String sql, Serializable bean) {
|
||||
return dataSource().nativeUpdateAsync(sql, bean);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.*;
|
||||
import org.redkale.asm.AsmDepends;
|
||||
import static org.redkale.source.DataResultSet.formatColumnValue;
|
||||
import org.redkale.util.*;
|
||||
|
||||
@@ -20,6 +21,7 @@ import org.redkale.util.*;
|
||||
* @author zhangjx
|
||||
* @since 2.8.0
|
||||
*/
|
||||
@AsmDepends
|
||||
public interface DataSqlSource extends DataSource {
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,9 +3,48 @@
|
||||
*/
|
||||
package org.redkale.source.spi;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.IntFunction;
|
||||
import org.redkale.asm.AnnotationVisitor;
|
||||
import org.redkale.asm.AsmMethodBean;
|
||||
import org.redkale.asm.AsmMethodBoost;
|
||||
import org.redkale.asm.ClassWriter;
|
||||
import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES;
|
||||
import org.redkale.asm.FieldVisitor;
|
||||
import org.redkale.asm.Label;
|
||||
import org.redkale.asm.MethodVisitor;
|
||||
import static org.redkale.asm.Opcodes.ACC_PRIVATE;
|
||||
import static org.redkale.asm.Opcodes.ACC_PUBLIC;
|
||||
import static org.redkale.asm.Opcodes.ACC_SUPER;
|
||||
import static org.redkale.asm.Opcodes.ALOAD;
|
||||
import static org.redkale.asm.Opcodes.ARETURN;
|
||||
import static org.redkale.asm.Opcodes.ASTORE;
|
||||
import static org.redkale.asm.Opcodes.DUP;
|
||||
import static org.redkale.asm.Opcodes.GETFIELD;
|
||||
import static org.redkale.asm.Opcodes.INVOKEINTERFACE;
|
||||
import static org.redkale.asm.Opcodes.INVOKESPECIAL;
|
||||
import static org.redkale.asm.Opcodes.INVOKEVIRTUAL;
|
||||
import static org.redkale.asm.Opcodes.NEW;
|
||||
import static org.redkale.asm.Opcodes.POP;
|
||||
import static org.redkale.asm.Opcodes.RETURN;
|
||||
import static org.redkale.asm.Opcodes.V11;
|
||||
import org.redkale.asm.Type;
|
||||
import org.redkale.persistence.Sql;
|
||||
import org.redkale.source.AbstractDataSqlSource;
|
||||
import org.redkale.source.DataNativeSqlInfo;
|
||||
import org.redkale.source.DataNativeSqlParser;
|
||||
import org.redkale.source.DataSqlMapper;
|
||||
import org.redkale.source.DataSqlSource;
|
||||
import org.redkale.source.SourceException;
|
||||
import org.redkale.util.RedkaleClassLoader;
|
||||
import org.redkale.util.TypeToken;
|
||||
import org.redkale.util.Utility;
|
||||
|
||||
/**
|
||||
* DataSqlMapper工厂类
|
||||
@@ -20,10 +59,215 @@ import org.redkale.source.DataSqlSource;
|
||||
*/
|
||||
public final class DataSqlMapperBuilder {
|
||||
|
||||
private static Map<String, AsmMethodBean> baseMethodBeans;
|
||||
|
||||
private DataSqlMapperBuilder() {
|
||||
}
|
||||
|
||||
public static <T, M extends DataSqlMapper<T>> M createMapper(DataNativeSqlParser nativeSqlParser, DataSqlSource source, Class<M> mapperType) {
|
||||
return null;
|
||||
if (!mapperType.isInterface()) {
|
||||
throw new SourceException(mapperType + " is not interface");
|
||||
}
|
||||
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
final Class entityType = entityType(mapperType);
|
||||
final String supDynName = mapperType.getName().replace('.', '/');
|
||||
final String newDynName = "org/redkaledyn/source/mapper/_DynDataSqlMapper__" + supDynName.replace('$', '_');
|
||||
try {
|
||||
Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.'));
|
||||
Class newClazz = clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz;
|
||||
M mapper = (M) newClazz.getDeclaredConstructor().newInstance();
|
||||
{
|
||||
Field c = newClazz.getDeclaredField("_source");
|
||||
c.setAccessible(true);
|
||||
c.set(mapper, source);
|
||||
}
|
||||
{
|
||||
Field c = newClazz.getDeclaredField("_type");
|
||||
c.setAccessible(true);
|
||||
c.set(mapper, entityType);
|
||||
}
|
||||
return mapper;
|
||||
} catch (ClassNotFoundException e) {
|
||||
//do nothing
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
if (baseMethodBeans == null) {
|
||||
baseMethodBeans = AsmMethodBoost.getMethodBeans(DataSqlMapper.class);
|
||||
}
|
||||
List<Item> items = new ArrayList<>();
|
||||
Map<String, AsmMethodBean> selfMethodBeans = AsmMethodBoost.getMethodBeans(mapperType);
|
||||
for (Method method : mapperType.getMethods()) {
|
||||
if (Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
if ("dataSource".equals(method.getName()) && method.getParameterCount() == 0) {
|
||||
continue;
|
||||
}
|
||||
if ("entityType".equals(method.getName()) && method.getParameterCount() == 0) {
|
||||
continue;
|
||||
}
|
||||
Sql sql = method.getAnnotation(Sql.class);
|
||||
if (sql == null) {
|
||||
if (Modifier.isAbstract(method.getModifiers())) {
|
||||
throw new SourceException(method + " require @" + Sql.class.getSimpleName());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!Modifier.isAbstract(method.getModifiers())) {
|
||||
throw new SourceException(method + " is not abstract, but contains @" + Sql.class.getSimpleName());
|
||||
}
|
||||
IntFunction<String> signFunc = null;
|
||||
if (source instanceof AbstractDataSqlSource) {
|
||||
signFunc = ((AbstractDataSqlSource) source).getSignFunc();
|
||||
}
|
||||
DataNativeSqlInfo sqlInfo = nativeSqlParser.parse(signFunc, source.getType(), sql.value());
|
||||
AsmMethodBean methodBean = selfMethodBeans.get(AsmMethodBoost.getMethodBeanKey(method));
|
||||
if (!Utility.equalsElement(sqlInfo.getRootParamNames(), methodBean.fieldNameList())) {
|
||||
throw new SourceException(method + " parameters not match @" + Sql.class.getSimpleName() + "(" + sql.value() + ")");
|
||||
}
|
||||
items.add(new Item(method, sqlInfo, methodBean));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
final String entityDesc = Type.getDescriptor(entityType);
|
||||
final String sqlSourceDesc = Type.getDescriptor(DataSqlSource.class);
|
||||
|
||||
ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
|
||||
FieldVisitor fv;
|
||||
MethodVisitor mv;
|
||||
AnnotationVisitor av0;
|
||||
|
||||
cw.visit(V11, ACC_PUBLIC + ACC_SUPER, newDynName, null, "java/lang/Object", new String[]{supDynName});
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "_source", sqlSourceDesc, null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, "_type", "Ljava/lang/Class;", null, null);
|
||||
fv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "dataSource", "()Lorg/redkale/source/DataSqlSource;", null, null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, "org/redkale/test/source/parser/DynForumInfoMapperImpl", "source", sqlSourceDesc);
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "entityType", "()Ljava/lang/Class;", "()Ljava/lang/Class<" + entityDesc + ">;", null);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitFieldInsn(GETFIELD, newDynName, "type", "Ljava/lang/Class;");
|
||||
mv.visitInsn(ARETURN);
|
||||
mv.visitMaxs(1, 1);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
//sql系列方法
|
||||
for (Item item : items) {
|
||||
Method method = item.method;
|
||||
DataNativeSqlInfo sqlInfo = item.sqlInfo;
|
||||
AsmMethodBean methodBean = item.methodBean;
|
||||
Sql sql = method.getAnnotation(Sql.class);
|
||||
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "queryForumResultAsync", "(Lorg/redkale/test/source/parser/ForumBean;)Ljava/util/concurrent/CompletableFuture;", "(Lorg/redkale/test/source/parser/ForumBean;)Ljava/util/concurrent/CompletableFuture<Ljava/util/List<Lorg/redkale/test/source/parser/ForumResult;>;>;", null);
|
||||
Label l0 = new Label();
|
||||
mv.visitLabel(l0);
|
||||
mv.visitLdcInsn(sql.value());
|
||||
mv.visitVarInsn(ASTORE, 2);
|
||||
Label l1 = new Label();
|
||||
mv.visitLabel(l1);
|
||||
mv.visitTypeInsn(NEW, "java/util/HashMap");
|
||||
mv.visitInsn(DUP);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V", false);
|
||||
mv.visitVarInsn(ASTORE, 3);
|
||||
Label l2 = new Label();
|
||||
mv.visitLabel(l2);
|
||||
mv.visitVarInsn(ALOAD, 3);
|
||||
mv.visitLdcInsn("bean");
|
||||
mv.visitVarInsn(ALOAD, 1);
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true);
|
||||
mv.visitInsn(POP);
|
||||
Label l3 = new Label();
|
||||
mv.visitLabel(l3);
|
||||
mv.visitVarInsn(ALOAD, 0);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, "org/redkale/test/source/parser/DynForumInfoMapperImpl", "dataSource", "()Lorg/redkale/source/DataSqlSource;", false);
|
||||
mv.visitLdcInsn(Type.getType("Lorg/redkale/test/source/parser/ForumResult;"));
|
||||
mv.visitVarInsn(ALOAD, 2);
|
||||
mv.visitVarInsn(ALOAD, 3);
|
||||
mv.visitMethodInsn(INVOKEINTERFACE, "org/redkale/source/DataSqlSource", "nativeQueryListAsync", "(Ljava/lang/Class;Ljava/lang/String;Ljava/util/Map;)Ljava/util/concurrent/CompletableFuture;", true);
|
||||
mv.visitInsn(ARETURN);
|
||||
Label l4 = new Label();
|
||||
mv.visitLabel(l4);
|
||||
mv.visitLocalVariable("this", "Lorg/redkale/test/source/parser/DynForumInfoMapperImpl;", null, l0, l4, 0);
|
||||
mv.visitLocalVariable("bean", "Lorg/redkale/test/source/parser/ForumBean;", null, l0, l4, 1);
|
||||
mv.visitLocalVariable("sql", "Ljava/lang/String;", null, l1, l4, 2);
|
||||
mv.visitLocalVariable("params", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;", l2, l4, 3);
|
||||
mv.visitMaxs(4, 4);
|
||||
mv.visitEnd();
|
||||
}
|
||||
|
||||
cw.visitEnd();
|
||||
|
||||
byte[] bytes = cw.toByteArray();
|
||||
Class<?> newClazz = new ClassLoader(loader) {
|
||||
public final Class<?> loadClass(String name, byte[] b) {
|
||||
return defineClass(name, b, 0, b.length);
|
||||
}
|
||||
}.loadClass(newDynName.replace('/', '.'), bytes);
|
||||
RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz);
|
||||
RedkaleClassLoader.putReflectionPublicConstructors(newClazz, newDynName.replace('/', '.'));
|
||||
RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.'));
|
||||
try {
|
||||
M mapper = (M) newClazz.getDeclaredConstructor().newInstance();
|
||||
{
|
||||
Field c = newClazz.getDeclaredField("_source");
|
||||
c.setAccessible(true);
|
||||
c.set(mapper, source);
|
||||
}
|
||||
{
|
||||
Field c = newClazz.getDeclaredField("_type");
|
||||
c.setAccessible(true);
|
||||
c.set(mapper, entityType);
|
||||
}
|
||||
return mapper;
|
||||
} catch (Exception ex) {
|
||||
throw new SourceException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static Class entityType(Class mapperType) {
|
||||
for (java.lang.reflect.Type t : mapperType.getGenericInterfaces()) {
|
||||
if (DataSqlMapper.class.isAssignableFrom(TypeToken.typeToClass(t))) {
|
||||
return TypeToken.typeToClass(((ParameterizedType) t).getActualTypeArguments()[0]);
|
||||
}
|
||||
}
|
||||
throw new SourceException("Not found entity class from " + mapperType.getName());
|
||||
}
|
||||
|
||||
private static class Item {
|
||||
|
||||
public Method method;
|
||||
|
||||
public DataNativeSqlInfo sqlInfo;
|
||||
|
||||
public AsmMethodBean methodBean;
|
||||
|
||||
public Item(Method method, DataNativeSqlInfo sqlInfo, AsmMethodBean methodBean) {
|
||||
this.method = method;
|
||||
this.sqlInfo = sqlInfo;
|
||||
this.methodBean = methodBean;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,9 +516,14 @@ public class SourceModuleEngine extends ModuleEngine implements SourceManager {
|
||||
if ((srcObj instanceof Service) && Sncp.isRemote((Service) srcObj)) {
|
||||
return null; //远程模式不得注入
|
||||
}
|
||||
DataSqlMapper old = resourceFactory.find(resourceName, DataSqlMapper.class);
|
||||
if (old != null) {
|
||||
return old;
|
||||
}
|
||||
DataSource source = loadDataSource(resourceName, false);
|
||||
Class mapperType = field.getType();
|
||||
DataSqlMapper mapper = DataSqlMapperBuilder.createMapper(nativeSqlParser, (DataSqlSource) source, mapperType);
|
||||
resourceFactory.register(resourceName, DataSqlMapper.class, mapper);
|
||||
field.set(srcObj, mapper);
|
||||
return mapper;
|
||||
} catch (Exception e) {
|
||||
|
||||
Reference in New Issue
Block a user