This commit is contained in:
redkale
2024-01-09 23:22:12 +08:00
parent 4de3d3f505
commit ce630ee253
16 changed files with 963 additions and 95 deletions

View File

@@ -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;

View 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();
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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 {
/**

View File

@@ -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;
}
}
}

View File

@@ -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) {