Calcite使用外部自定义函数

news2024/11/26 18:46:12

在calcite中可以通过自定义函数来实现丰富的算子,但是往往需要将自定义的函数包与Calcite集成在一起,才能使用相应的算子功能,由于业务需求多变,函数包可能面临频繁的更改,如果需要更改生效,则需要将Calcite的代码重新打包,并重启jvm才能获取最新的算子功能。那么能否更改现状,更新完函数包,通过api调用刷新注册函数呢?

首先我们引入一个外部的函数包。

D:\maven-3.6.3\repository\com\kanyun\func\1.0-SNAPSHOT\func-1.0-SNAPSHOT.jar

该包中只包含一个class。

package com.kanyun.func.string;
import java.util.Stack;

public class StringFuncUtil {

    /**
     * 字符串反转
     *
     * @param s
     * @return
     */
    public static String reverse(String s) {
        char[] str = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        for (char aStr1 : str) {
            stack.push(aStr1);
        }
        StringBuilder reversed = new StringBuilder();
        while (!stack.isEmpty()) {
            reversed.append(stack.pop());
        }
        return reversed.toString();
    }
}

 可以看到com.kanyun.func.string.StringFuncUtil#reverse() 就是我们自定义的算子。

此时我们需要将该函数注册到calcite中。

由于该函数并不在我们calcite服务的classpath下,所以是无法直接进行注册的。

//java.lang.ClassNotFoundException: com.kanyun.func.string.StringFuncUtil
Class.forName("com.kanyun.func.string.StringFuncUtil")

因此第一步我们需要先加载这个函数包,最简单的形式是使用URLClassLoader来加载这个函数包,这里我自定义了一个类加载器来加载这个函数包

package com.kanyun.sql.func;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;

/**
 * 外部函数类加载器,单例模式。保证该类加载器在jvm中只存在一个实例
 * 此处配置类加载器主要是用来外部函数(非ClassPath中的jar)
 */
public class ExternalFuncClassLoader extends URLClassLoader {

    private static ExternalFuncClassLoader externalFuncClassLoader;

    /**
     * 私有的构造方法,禁止外部通过new方法创建类加载器实例
     * @param urls
     * @param parent
     */
    private ExternalFuncClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }


    /**
     * 创建自定义类加载器实例
     *
     * @param urls
     * @param parent
     * @return
     */
    public static ExternalFuncClassLoader newInstance(URL[] urls, ClassLoader parent) {
        if (externalFuncClassLoader == null) {
//            如果传递的父加载器为null,则继承线程上下文类加载器
            externalFuncClassLoader = new ExternalFuncClassLoader(urls, parent == null ? Thread.currentThread().getContextClassLoader() : parent);

        }
        return externalFuncClassLoader;
    }

    /**
     * 添加jar
     *
     * @param files
     * @throws MalformedURLException
     */
    public void addJars(List<File> files) throws MalformedURLException {
        for (File file : files) {
            super.addURL(file.toURI().toURL());
        }
    }

    /**
     * 获取类加载器实例
     * @return
     */
    public static ExternalFuncClassLoader getInstance() {
        if (externalFuncClassLoader == null) {
            URL[] urls = {};
            externalFuncClassLoader = new ExternalFuncClassLoader(urls,  Thread.currentThread().getContextClassLoader() );

        }
        return externalFuncClassLoader;
    }
}

需要注意这个自定义类加载器是一个单例模式,这也就意味着,我们在创建和使用这个类加载器时是同一个对象。

创建类加载器

    /**
     * 从文件URL获取类加载器
     * 一次加载所有的外部函数,使用同一个类加载器,而不是加载一个外部jar就创建一个类加载器
     *
     * @param files
     * @return
     * @throws MalformedURLException
     */
    protected ClassLoader createClassLoaderFromFile(List<File> files) throws MalformedURLException {
        URL[] urls = new URL[files.size()];
        for (int i = 0; i < files.size(); i++) {
            urls[i] = files.get(i).toURI().toURL();
        }
        ExternalFuncClassLoader urlClassLoader = ExternalFuncClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader());
        return urlClassLoader;
    }

使用创建出来的ClassLoader来解析jar包中的内容,并缓存其中的由public static修饰的方法。

在上面的函数包中,它将缓存com.kanyun.func.string.StringFuncUtil#reverse()方法来作为自定义函数使用!

    /**
     * 解析jar包
     * 并取出需要的函数,这里仅取出 public static 修饰的方法
     *
     * @param jarFile
     * @param classLoader
     * @throws ClassNotFoundException
     */
    private void parseJar(JarFile jarFile, ClassLoader classLoader) throws ClassNotFoundException {
        log.info("准备解析jar文件:{}", jarFile.getName());
//        得到jar包中的元素,包括目录
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry jarEntry = entries.nextElement();
//            判断是否是目录
            if (jarEntry.isDirectory()) continue;
            String fullName = jarEntry.getName();
//            判断文件是否是class文件
            if (!fullName.endsWith(".class")) continue;
            String className = fullName.substring(0, fullName.length() - 6).replaceAll("/", ".");
            Class<?> clazz;
            if (classLoader == null) {
//                对于当前非jvm进程的类路径来说,不指定类加载器是会报错的:java.lang.ClassNotFoundException,因为默认的类加载器是AppClassLoader,它加载classPath下的jar
                clazz = Class.forName(className);
            } else {
                clazz = Class.forName(className, true, classLoader);
            }
            cacheFunc(clazz, userDefineFunctions);
        }
    }

calcite自定义函数注册

 /**
     * 注册函数
     */
    public static void registerFunction(SchemaPlus schemaPlus) {
        log.debug("=======Schema:[{}],开始注册函数======", schemaPlus.getName());
        Set<Map.Entry<String, Class>> entries = userDefineFunctions.entrySet();
        entries.addAll(innerDefineFunctions.entrySet());
        for (Map.Entry<String, Class> entry : entries) {
            log.debug("待注册的函数信息:[{}.{}()]", entry.getValue().getName(), entry.getKey());
            try {
//                第一个参数为在SQL中使用的函数名,第二个参数是传入类及类的方法名所创建的函数实例.
//                例如:自定义的函数为com.kanyun.fun.CustomFunc#applyDate() 但是在写SQL时希望把函数名写为APPLY_DATE,此时第一个参数应为APPLY_DATE
                schemaPlus.add(entry.getKey(), ScalarFunctionImpl.create(entry.getValue(), entry.getKey()));
            } catch (Exception e) {
                log.error("函数注册异常!:", e);
            }
        }
    }

自此我们的代码还算完美,实现了自定义函数的注册功能。

那么我们实际使用下这个自定义函数看一下:

# reverse()即为我们自定义的算子
select id,reverse(title) from DB1.girl_pic

发现报错了。

java.sql.SQLException: Error while executing SQL "select id,reverse(title) from DB1.girl_pic": Error while compiling generated Java code:
public org.apache.calcite.linq4j.Enumerable bind(final org.apache.calcite.DataContext root) {
  final org.apache.calcite.linq4j.Enumerable _inputEnumerable = org.apache.calcite.schema.Schemas.enumerable((org.apache.calcite.schema.ScannableTable) root.getRootSchema().getSubSchema("DB1").getTable("girl_pic"), root);
  return new org.apache.calcite.linq4j.AbstractEnumerable(){
      public org.apache.calcite.linq4j.Enumerator enumerator() {
        return new org.apache.calcite.linq4j.Enumerator(){
            public final org.apache.calcite.linq4j.Enumerator inputEnumerator = _inputEnumerable.enumerator();
            public void reset() {
              inputEnumerator.reset();
            }

            public boolean moveNext() {
              return inputEnumerator.moveNext();
            }

            public void close() {
              inputEnumerator.close();
            }

            public Object current() {
              final Object[] current = (Object[]) inputEnumerator.current();
              return new Object[] {
                  current[0],
                  com.kanyun.func.string.StringFuncUtil.reverse(current[1] == null ? (String) null : current[1].toString())};
            }

          };
      }

    };
}


public Class getElementType() {
  return java.lang.Object[].class;
}



	at org.apache.calcite.avatica.Helper.createException(Helper.java:56) ~[avatica-core-1.23.0.jar:1.23.0]
	at org.apache.calcite.avatica.Helper.createException(Helper.java:41) ~[avatica-core-1.23.0.jar:1.23.0]
	at org.apache.calcite.avatica.AvaticaStatement.executeInternal(AvaticaStatement.java:164) ~[avatica-core-1.23.0.jar:1.23.0]
	at org.apache.calcite.avatica.AvaticaStatement.executeQuery(AvaticaStatement.java:228) ~[avatica-core-1.23.0.jar:1.23.0]
	at com.kanyun.sql.SqlExecutor.execute(SqlExecutor.java:137) ~[classes/:?]
	at com.kanyun.ui.event.ExecuteSqlService$1.call(ExecuteSqlService.java:69) ~[classes/:?]
	at com.kanyun.ui.event.ExecuteSqlService$1.call(ExecuteSqlService.java:66) ~[classes/:?]
	at javafx.concurrent.Task$TaskCallable.call(Task.java:1423) ~[jfxrt.jar:?]
	at java.util.concurrent.FutureTask.run$$$capture(FutureTask.java:266) ~[?:1.8.0_261]
	at java.util.concurrent.FutureTask.run(FutureTask.java) ~[?:1.8.0_261]
	at javafx.concurrent.Service.lambda$null$6(Service.java:725) ~[jfxrt.jar:?]
	at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_261]
	at javafx.concurrent.Service.lambda$executeTask$7(Service.java:724) ~[jfxrt.jar:?]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[?:1.8.0_261]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[?:1.8.0_261]
	at java.lang.Thread.run(Thread.java:748) ~[?:1.8.0_261]
Caused by: java.lang.RuntimeException: Error while compiling generated Java code:
public org.apache.calcite.linq4j.Enumerable bind(final org.apache.calcite.DataContext root) {
  final org.apache.calcite.linq4j.Enumerable _inputEnumerable = org.apache.calcite.schema.Schemas.enumerable((org.apache.calcite.schema.ScannableTable) root.getRootSchema().getSubSchema("DB1").getTable("girl_pic"), root);
  return new org.apache.calcite.linq4j.AbstractEnumerable(){
      public org.apache.calcite.linq4j.Enumerator enumerator() {
        return new org.apache.calcite.linq4j.Enumerator(){
            public final org.apache.calcite.linq4j.Enumerator inputEnumerator = _inputEnumerable.enumerator();
            public void reset() {
              inputEnumerator.reset();
            }

            public boolean moveNext() {
              return inputEnumerator.moveNext();
            }

            public void close() {
              inputEnumerator.close();
            }

            public Object current() {
              final Object[] current = (Object[]) inputEnumerator.current();
              return new Object[] {
                  current[0],
                  com.kanyun.func.string.StringFuncUtil.reverse(current[1] == null ? (String) null : current[1].toString())};
            }

          };
      }

    };
}


public Class getElementType() {
  return java.lang.Object[].class;
}



	at org.apache.calcite.avatica.Helper.wrap(Helper.java:37) ~[avatica-core-1.23.0.jar:1.23.0]
	at org.apache.calcite.adapter.enumerable.EnumerableInterpretable.toBindable(EnumerableInterpretable.java:128) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl$CalcitePreparingStmt.implement(CalcitePrepareImpl.java:1159) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.Prepare.prepareSql(Prepare.java:324) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.Prepare.prepareSql(Prepare.java:220) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl.prepare2_(CalcitePrepareImpl.java:665) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl.prepare_(CalcitePrepareImpl.java:519) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl.prepareSql(CalcitePrepareImpl.java:487) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.jdbc.CalciteConnectionImpl.parseQuery(CalciteConnectionImpl.java:236) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.jdbc.CalciteMetaImpl.prepareAndExecute(CalciteMetaImpl.java:621) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.avatica.AvaticaConnection.prepareAndExecuteInternal(AvaticaConnection.java:677) ~[avatica-core-1.23.0.jar:1.23.0]
	at org.apache.calcite.avatica.AvaticaStatement.executeInternal(AvaticaStatement.java:157) ~[avatica-core-1.23.0.jar:1.23.0]
	... 13 more
Caused by: org.codehaus.commons.compiler.CompileException: Line 24, Column 57: Unknown variable or type "com.kanyun.func.string.StringFuncUtil"
	at org.codehaus.janino.UnitCompiler.compileError(UnitCompiler.java:12969) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.getType2(UnitCompiler.java:7162) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$14300(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$23.visitPackage(UnitCompiler.java:6647) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$23.visitPackage(UnitCompiler.java:6644) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$Package.accept(Java.java:4627) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.getType(UnitCompiler.java:6644) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.getType2(UnitCompiler.java:7157) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$18100(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$26.visitAmbiguousName(UnitCompiler.java:6748) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$26.visitAmbiguousName(UnitCompiler.java:6747) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$AmbiguousName.accept(Java.java:4603) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.getType(UnitCompiler.java:6747) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$15100(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$25.visitLvalue(UnitCompiler.java:6708) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$25.visitLvalue(UnitCompiler.java:6705) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$Lvalue.accept(Java.java:4528) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.getType(UnitCompiler.java:6705) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$14400(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$23.visitRvalue(UnitCompiler.java:6653) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$23.visitRvalue(UnitCompiler.java:6644) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$Rvalue.accept(Java.java:4495) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.getType(UnitCompiler.java:6644) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.findIMethod(UnitCompiler.java:9355) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet2(UnitCompiler.java:5195) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$9300(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitMethodInvocation(UnitCompiler.java:4698) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitMethodInvocation(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$MethodInvocation.accept(Java.java:5470) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5817) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$3800(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$7.visitRvalue(UnitCompiler.java:2729) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$7.visitRvalue(UnitCompiler.java:2717) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$Rvalue.accept(Java.java:4498) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:2717) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5787) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet2(UnitCompiler.java:5760) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$9900(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitNewInitializedArray(UnitCompiler.java:4709) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitNewInitializedArray(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$NewInitializedArray.accept(Java.java:5770) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5817) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:2811) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$2800(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1573) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1558) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$ReturnStatement.accept(Java.java:3888) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:1558) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileStatements(UnitCompiler.java:1644) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:3621) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:3292) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1430) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1403) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:829) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:1026) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:996) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$200(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$3.visitAnonymousClassDeclaration(UnitCompiler.java:420) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$3.visitAnonymousClassDeclaration(UnitCompiler.java:418) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$AnonymousClassDeclaration.accept(Java.java:1377) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:418) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet2(UnitCompiler.java:5675) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$9700(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4707) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$NewAnonymousClassInstance.accept(Java.java:5635) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5817) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:2811) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$2800(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1573) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1558) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$ReturnStatement.accept(Java.java:3888) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:1558) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileStatements(UnitCompiler.java:1644) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:3621) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:3292) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1430) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1403) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:829) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:1026) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:996) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$200(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$3.visitAnonymousClassDeclaration(UnitCompiler.java:420) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$3.visitAnonymousClassDeclaration(UnitCompiler.java:418) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$AnonymousClassDeclaration.accept(Java.java:1377) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:418) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet2(UnitCompiler.java:5675) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$9700(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4707) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$16.visitNewAnonymousClassInstance(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$NewAnonymousClassInstance.accept(Java.java:5635) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGet(UnitCompiler.java:4674) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileGetValue(UnitCompiler.java:5817) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:2811) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$2800(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1573) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$6.visitReturnStatement(UnitCompiler.java:1558) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$ReturnStatement.accept(Java.java:3888) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:1558) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileStatements(UnitCompiler.java:1644) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:3621) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:3292) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1430) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileDeclaredMethods(UnitCompiler.java:1403) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:829) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:442) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$400(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$3.visitPackageMemberClassDeclaration(UnitCompiler.java:422) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$3.visitPackageMemberClassDeclaration(UnitCompiler.java:418) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$PackageMemberClassDeclaration.accept(Java.java:1688) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile(UnitCompiler.java:418) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compile2(UnitCompiler.java:392) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.access$000(UnitCompiler.java:236) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$2.visitCompilationUnit(UnitCompiler.java:363) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler$2.visitCompilationUnit(UnitCompiler.java:361) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.Java$CompilationUnit.accept(Java.java:371) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.UnitCompiler.compileUnit(UnitCompiler.java:361) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.SimpleCompiler.cook(SimpleCompiler.java:264) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.SimpleCompiler.compileToClassLoader(SimpleCompiler.java:517) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.SimpleCompiler.cook(SimpleCompiler.java:241) ~[janino-3.1.8.jar:?]
	at org.codehaus.janino.SimpleCompiler.cook(SimpleCompiler.java:219) ~[janino-3.1.8.jar:?]
	at org.codehaus.commons.compiler.Cookable.cook(Cookable.java:82) ~[commons-compiler-3.1.8.jar:?]
	at org.codehaus.commons.compiler.Cookable.cook(Cookable.java:77) ~[commons-compiler-3.1.8.jar:?]
	at org.apache.calcite.adapter.enumerable.EnumerableInterpretable.compileToBindable(EnumerableInterpretable.java:174) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.adapter.enumerable.EnumerableInterpretable.getBindable(EnumerableInterpretable.java:168) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.adapter.enumerable.EnumerableInterpretable.toBindable(EnumerableInterpretable.java:125) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl$CalcitePreparingStmt.implement(CalcitePrepareImpl.java:1159) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.Prepare.prepareSql(Prepare.java:324) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.Prepare.prepareSql(Prepare.java:220) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl.prepare2_(CalcitePrepareImpl.java:665) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl.prepare_(CalcitePrepareImpl.java:519) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.prepare.CalcitePrepareImpl.prepareSql(CalcitePrepareImpl.java:487) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.jdbc.CalciteConnectionImpl.parseQuery(CalciteConnectionImpl.java:236) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.jdbc.CalciteMetaImpl.prepareAndExecute(CalciteMetaImpl.java:621) ~[calcite-core-1.34.0.jar:1.34.0]
	at org.apache.calcite.avatica.AvaticaConnection.prepareAndExecuteInternal(AvaticaConnection.java:677) ~[avatica-core-1.23.0.jar:1.23.0]
	at org.apache.calcite.avatica.AvaticaStatement.executeInternal(AvaticaStatement.java:157) ~[avatica-core-1.23.0.jar:1.23.0]
	... 13 more
2023-08-09 10:00:14.114 ERROR [JavaFX Application Thread] c.k.u.e.ExecuteSqlService                : 异步SQL执行任务异常:

这个错误我们分两部分看:

第一部分:

java.sql.SQLException: Error while executing SQL "select id,reverse(title) from DB1.girl_pic": Error while compiling generated Java code:
...................省略报错
java.sql.SQLException: Error while executing SQL "select id,reverse(title) from DB1.girl_pic": Error while compiling generated Java code:
public org.apache.calcite.linq4j.Enumerable bind(final org.apache.calcite.DataContext root) {

意思是janino在将sql编译成java代码报错(calcite利用janino生成代码这块不再细说了)

第二部分:

........省略报错信息
Caused by: org.codehaus.commons.compiler.CompileException: Line 24, Column 57: Unknown variable or type "com.kanyun.func.string.StringFuncUtil"
	at org.codehaus.janino.UnitCompiler.compileError(UnitCompiler.java:12969) ~[janino-3.1.8.jar:?]
........省略报错信息

意思是报错的原因是未找到变量:com.kanyun.func.string.StringFuncUtil而这个变量正是我们自定义函数所在的类!

经过调试发现报错的位置是:类org.codehaus.janino.UnitCompiler#getType()

    private IType
    getType(Atom a) throws CompileException {

        IType result = (IType) a.accept(new AtomVisitor<IType, CompileException>() {
            // 此处报错
            @Override public IType
            visitPackage(Package p) throws CompileException { return UnitCompiler.this.getType2(p); }

            @Override @Nullable public IType
            visitType(Type t) throws CompileException { return UnitCompiler.this.getType(t); }

            @Override @Nullable public IType
            visitRvalue(Rvalue rv) throws CompileException { return UnitCompiler.this.getType(rv); }

            @Override @Nullable public IType
            visitConstructorInvocation(ConstructorInvocation ci) throws CompileException {
                return UnitCompiler.this.getType2(ci);
            }
        });

        assert result != null;
        return result;
    }

其中Atom的参数即为我们自定义函数所在的类 

其中Atom是一个典型的访问者模式

而报错的这个地方是janino框架,janino简介

因此猜想这个报错原因是加载不到我们自定义函数所在的类:com.kanyun.func.string.StringFuncUtil

虽然我们通过自定义类加载器加载了自定义的外部函数包,但是janino执行代码的时候使用的不一定是我们自定义的类加载器,所以有可能加载不到,经过调试发现,当前执行代码块的类加载器是AppClassLoader,也即Atom(我们的自定义类加载器是AppClassLoader)

 但实际上这个类并不是AppClassLoader来加载的,而是我们使用上面的自定义类加载器加载的。

此时忽然想到自己的业务代码调用Calcite执行SQL是异步的,或许janino并不能使用自定义的类加载器。

不过既然找到了报错的地方是janino框架,那么Calcite又是如何调用Janino的呢?

经过一段时间查阅资料,发现在calcite 模块的类org.apache.calcite.rel.metadata.JaninoRelMetadataProvider#compile()方法调用了Janino

而调用了org.apache.calcite.rel.metadata.JaninoRelMetadataProvider#compile()的方法是,

org.apache.calcite.rel.metadata.JaninoRelMetadataProvider#generateCompileAndInstantiate()方法

  private static <MH extends MetadataHandler<?>> MH generateCompileAndInstantiate(
      Class<MH> handlerClass,
      List<? extends MetadataHandler<? extends Metadata>> handlers) {

    final List<? extends MetadataHandler<? extends Metadata>> uniqueHandlers = handlers.stream()
        .distinct()
        .collect(Collectors.toList());
    // 生成Handler,字符串拼接生成代码
    RelMetadataHandlerGeneratorUtil.HandlerNameAndGeneratedCode handlerNameAndGeneratedCode =
        RelMetadataHandlerGeneratorUtil.generateHandler(handlerClass, uniqueHandlers);

    try {
      // 最终编译上面的handler
      return compile(handlerNameAndGeneratedCode.getHandlerName(),
          handlerNameAndGeneratedCode.getGeneratedCode(), handlerClass, uniqueHandlers);
    } catch (CompileException | IOException e) {
      throw new RuntimeException("Error compiling:\n"
          + handlerNameAndGeneratedCode.getGeneratedCode(), e);
    }
  }

从方法名可以看出是生成和编译实例类(该方法在旧版本的calcite中是load3()方法)

RelMetadataQuery这个类看名字就可以判断出它是用于获取元数据的。但是获取具体元数据的方法实现并不是直接依靠RelMetadataQuery。RelMetadataQuery对象有很多handler对象,获取某一方面类型的元数据都需要依靠对应的handler。

在calcite的源码中是找不到这些handler的实现的,只能找到这些handler的接口。 calcite内部很多地方使用了动态代码生成和编译。这些handler了也不例外也是在运行时生成具体实现并且编译后实例化对象的。 而这些handler的代码生成都在JaninoRelMetadataProvider#load3方法中实现

这里我使用的Calcite版本是:1.34.0

跑题了,类加载的问题还没解决,我们重点还是看一下org.apache.calcite.rel.metadata.JaninoRelMetadataProvider#compile()方法

  static  <MH extends MetadataHandler<?>> MH compile(String className,
      String generatedCode, Class<MH> handlerClass,
      List<? extends Object> argList) throws CompileException, IOException {
    // 定义编译器工厂类(janino模块)
    final ICompilerFactory compilerFactory;
    ClassLoader classLoader =
        Objects.requireNonNull(JaninoRelMetadataProvider.class.getClassLoader(), "classLoader");
    try {
      // 获取编译器工厂类(janino模块)
      compilerFactory = CompilerFactoryFactory.getDefaultCompilerFactory(classLoader);
    } catch (Exception e) {
      throw new IllegalStateException(
          "Unable to instantiate java compiler", e);
    }
    
     // 创建一个新的编译器(janino模块) ISimpleCompiler 是一个接口
    final ISimpleCompiler compiler = compilerFactory.newSimpleCompiler();
    // 设置创建出来的编译器的父类加载器(janino模块,重要)
    compiler.setParentClassLoader(JaninoRexCompiler.class.getClassLoader());

    if (CalciteSystemProperty.DEBUG.value()) {
      // Add line numbers to the generated janino class
      compiler.setDebuggingInformation(true, true, true);
      System.out.println(generatedCode);
    }

    compiler.cook(generatedCode);
    final Constructor constructor;
    final Object o;
    try {
      constructor = compiler.getClassLoader().loadClass(className)
          .getDeclaredConstructors()[0];
      o = constructor.newInstance(argList.toArray());
    } catch (InstantiationException
        | IllegalAccessException
        | InvocationTargetException
        | ClassNotFoundException e) {
      throw new RuntimeException(e);
    }
    return handlerClass.cast(o);
  }

上面的代码描述了Calcite调用Janino编译代码的过程,其中有一个非常重要步骤:就是设置编译器的父类加载器的。通过debug我们发现编译器的具体实现是 org.codehaus.janino.SimpleCompiler类。同时我们查看下设置父类加载器的代码

    @Override public void
    setParentClassLoader(@Nullable ClassLoader parentClassLoader) {
        this.parentClassLoader = (
            parentClassLoader != null
            ? parentClassLoader
            : Thread.currentThread().getContextClassLoader()
        );
    }

通过这个方法的代码,我们发现了一个成员变量,即:org.codehaus.janino.SimpleCompiler#parentClassLoader 同时发现它有一个默认的实现,就是使用的是线程上下文类加载器

这也就意味着如果我们调用执行SQL之前先设置线程上下文类加载器,那么这个成员变量的值就应还是我们设置的线程上下文类加载器,还记得之前我们自定义的类加载器吗?由于自定义类加载器使用了单例模式,因此保证加载外部自定义的类加载器与我们将要设置的线程上下文类加载器是同一个实例,同时为了验证上下文类加载器是否设置成功,还应该在执行SQL之前验证一下类的加载是否存在问题。

......省略代码

//        由于sql执行是异步操作,因此需要在此处重新设置线程上下文类加载器
        Thread.currentThread().setContextClassLoader(ExternalFuncClassLoader.getInstance());
//       验证上下文类加载器是否设置成功(不报错即为设置成功)
 Class<?> clazz = Class.forName("com.kanyun.func.string.StringFuncUtil", false, Thread.currentThread().getContextClassLoader());

......省略代码

//        执行SQL
ResultSet resultSet = statement.executeQuery(sql);

此时继续执行SQL,由于我们在执行SQL前添加了验证代码,执行时是通过的,同时我们还在

org.codehaus.janino.SimpleCompiler#parentClassLoader属性上添加了断点,可以观察到

它的属性值已经是我们自定义的类加载器了

但是在执行SQL过程中发现还是报错了。并且错误与之前一致。依然还是找不到的类的问题

再次debug发现报错位置的的类加载器依然是AppclassLoader。可是在执行SQL之前明明已经设置了线程上下文类加载器了啊。

回到刚才设置父类加载器的位置通过观察发现:

org.codehaus.janino.SimpleCompiler#setParentClassLoader()方法可能会更改默认parentClassLoader

打上断点调试:

通过断点发现,org.codehaus.janino.SimpleCompiler#parentClassLoader属性的值被setParentClassLoader()方法给改变了。

查下setParentClassLoader()方法被谁调用了,由于上面已经找过,因此很容易找到调用位置。

org.apache.calcite.rel.metadata.JaninoRelMetadataProvider#compile()调用

 通过在此处打断点,发现代码确实执行到了此处。而且发现org.apache.calcite.rel.metadata.JaninoRelMetadataProvider类例属于Calcite模块。

其调用setParentClassLoader()方法的参数则是org.apache.calcite.interpreter.JaninoRexCompiler的类加载器。

这也同时说明了org.apache.calcite.interpreter.JaninoRexCompile的类加载器是AppClassLoader

而org.apache.calcite.interpreter.JaninoRexCompile也属于Calcite模块。

由上面的代码发现,janino的 

org.codehaus.janino.SimpleCompiler#parentClassLoader是获取到了我们的自定义类加载器,但是由于外部调用了org.codehaus.janino.SimpleCompiler#setParentClassLoader()改变了org.codehaus.janino.SimpleCompiler#parentClassLoader属性。
因此思路就比较清晰了
1:类org.apache.calcite.interpreter.JaninoRexCompiler的加载时机
2:是否可以更改org.apache.calcite.interpreter.JaninoRexCompiler的类加载器

通过排查并没有找到org.apache.calcite.interpreter.JaninoRexCompiler是什么时候被加载的

此时问题就比较尴尬了,我们使用的自定义类加载器虽然可以加载解析注册自定义的函数,但是当janino去编译生成代码的时候又使用了AppClassLoader,导致无法找到类,因此如何使AppClassLoader能够加载到外部的函数就成了首要问题。

首先我们看一下AppClassloader的源码,发现AppClassloader是一个静态内部类

在sun.misc.Launcher类下

 由于AppClassloader继承自URLClassLoader,因此我们可以将AppClassLoader的实例强转为URLClassLoader类型,然后反射调用addURL方法

因此在使用自定义类加载器加载解析注册自定义的外部函数的同时,也需要使用反射的形式使AppClassLoader能够加载到外部的自定义函数。最后试一下效果!成功执行了SQL

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/881187.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DatenLord X Segmentfault直播预告 l CURP协议的工业化实践

CURP协议 传统单数据中心解决方案无法满足跨数据中心的场景对性能和一致性的需求。DatenLord推出开源的分布式KV存储Xline&#xff0c;针对多数据中心场景&#xff0c;实现数据的高性能跨云、跨数据中心共享访问&#xff0c;并且保证数据的一致性&#xff0c;方便业务系统实现…

量子论公众号是如何半年做到10000粉的?

有不少人问我&#xff0c;“量子论”公众号是怎么运营的&#xff1f;“量子论”公众号是如何涨到1万粉的&#xff1f; 用这篇文章作为参考答案吧。 我是2月份开始落笔写这个号的&#xff0c;那个时候我已经在玩ChatGPT了。 随着AI火出圈&#xff0c;我打算分享一些ChatGPT使用经…

【LeetCode每日一题】——628.三个数的最大乘积

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 排序 二【题目难度】 简单 三【题目编号】 628.三个数的最大乘积 四【题目描述】 给你一个…

2023最新版本Activiti7系列-源码篇-初始化过程

源码分析 1.设计模式 1.1 命令模式 https://dpb-bobokaoya-sm.blog.csdn.net/article/details/89115420 1.2 责任链模式 https://dpb-bobokaoya-sm.blog.csdn.net/article/details/89077040 2.初始化过程 2.1 入口代码 我们在SpringBoot项目中来看Activiti7的源码。首先要…

DTCC 2023即将启幕 明天见!

8月16日-18日&#xff0c;由IT168联合旗下ITPUB、ChinaUnix两大技术社区主办的第14届中国数据库技术大会&#xff08;DTCC2023&#xff09;将在北京举行 作为国内云原生数据仓库代表厂商&#xff0c;酷克数据受邀亮相DTCC 2023&#xff0c;与广大数据库领域从业人士共同分享云…

财务人员学python有意义吗,财务会python好找工作吗

这篇文章主要介绍了财务方面python需要什么水平&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 财务是一个比较特殊的工作岗位&#xff0c;每天需要接触各种各样的数据&#x…

【数理知识】三维空间旋转矩阵的欧拉角表示法,四元数表示法,两者之间的转换,Matlab 代码实现

序号内容1【数理知识】自由度 degree of freedom 及自由度的计算方法2【数理知识】刚体 rigid body 及刚体的运动3【数理知识】刚体基本运动&#xff0c;平动&#xff0c;转动4【数理知识】向量数乘&#xff0c;内积&#xff0c;外积&#xff0c;matlab代码实现5【数理知识】最…

SpringBoot复习:(52)不再需要使用@EnableTransactionManagement的原因

在Spring项目中&#xff0c;要用事务&#xff0c;需要EnableTransactionManagement注解加Transactional注解。而在SpringBoot项目&#xff0c;有事务的自动配置类TransactionAutoConfiguration,代码如下&#xff1a; 可以在其内部类EnableTransactionManagementConfiguratio…

06 - 文件的差异和恢复

查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;GIT常用场景- 目录 文章目录 1. 文件的差异2. 文件的恢复 工作区&#xff1a;本地进行修改的code 暂存区&#xff1a;执行了 git add <filename> 命令之后 版本库&#xff1a;执行了 git commit <...>…

Redis数据结构——跳跃表

跳跃表 先来回顾常规的跳跃表。 &#xff01;&#xff01;&#xff01;下面的图片和内容都来自于这个链接Redis数据结构——跳跃表 - 随心所于 - 博客园 对于一个有序的链表&#xff0c;我们如何才能快速定位一个元素呢&#xff1f;只能遍历&#xff0c;时间复杂度是O(N)&…

Bitcoin 加速交易操作示例

这里以 Bitcoin Ordinals NFT 为例&#xff0c; 进行加速交易演示 第1步&#xff1a;新建子账户 温馨提示&#xff1a;如果有多条鱼未确认&#xff0c;也只需1个账户即可&#xff0c;不必搞多个子账户 第2步&#xff1a;切换回到老地址&#xff08;Account 1&#xff09; 第3步…

Springboot + Vue ElementUI 实现MySQLPostgresql可视化

一、功能展示&#xff1a; 效果如图&#xff1a; DB连接配置维护&#xff1a; Schema功能&#xff1a;集成Screw生成文档&#xff0c;导出库的表结构&#xff0c;导出表结构和数据 表对象操作&#xff1a;翻页查询&#xff0c;查看创建SQL&#xff0c;生成代码 可以单个代码文…

获取部门完整路径数据

1: 获取部门数据 (基础) SELECT id, CONCAT(pid_path,id) as pid_path2,pid_path,title FROM web_department;2: 获取部门数据 (进阶, 但是是,分隔) SELECT t1.id, t1.title, CONCAT(t1.pid_path, ,, t1.id) AS pid_path,(SELECT GROUP_CONCAT(t2.title ORDER BY FIND_IN_SET…

Unity 实现2D地面挖洞!涂抹地形(碰撞部分,方法二)

文章目录 前言一、初始化虚拟点1.1点结构:1.2每个点有的状态:1.3生成点结构: 二、实例化边缘碰撞盒2.1计算生成边缘碰撞盒 三、涂抹部分3.1.虚拟点3.2.鼠标点3.3.内圈3.4.外圈 四、关于优化结语: 前言 老规矩先上效果图 继上一篇涂抹地形文章讲解发出后&#xff0c;有不少网友…

与微服务平台厂家联手,一起实现高效率发展!

在如今的快节奏发展社会中&#xff0c;只有利用科技的力量&#xff0c;才能与市场接轨&#xff0c;了解市场和客户需求&#xff0c;最终实现更快速的发展。如果还停留在闭门造车的环境中&#xff0c;不“引进来&#xff0c;走出去”&#xff0c;那势必会与成功擦肩而过。微服务…

SpringSecurity结合电商项目

pom <!--SpringSecurity及JWT依赖配置--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</ artifactId></dependency> <!--Hutool Java工具包--> <dependency>&l…

面试热题(每日温度)

请根据每日 气温 列表 temperatures &#xff0c;重新生成一个列表&#xff0c;要求其对应位置的输出为&#xff1a;要想观测到更高的气温&#xff0c;至少需要等待的天数。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来代替。 输入: temperatures [73,74,75,71,69…

ADC实验

查看VR1链接的丝印&#xff1a;XadcAIN3 设置相关寄存器 使用的是通道3&#xff0c;要设置相应的通道寄存器 #include "exynos_4412.h"int main() {unsigned int AdcValue 0;/*将ADC的精度设置成 12bit*/ADCCON ADCCON | (1 << 16);/*使能ADC的分频器*…

Python Flask+Echarts+sklearn+MySQL(评论情感分析、用户推荐、BI报表)项目分享

Python FlaskEchartssklearnMySQL(评论情感分析、用户推荐、BI报表)项目分享 项目背景&#xff1a; 随着互联网的快速发展和智能手机的普及&#xff0c;人们越来越倾向于在网上查找餐厅、购物中心、酒店和旅游景点等商户的点评和评分信息&#xff0c;以便做出更好的消费决策。…

第三代网关,POE级联蓝牙网关VDB3601,至多可连接38台蓝牙设备

第三代蓝牙网关&#xff0c;网关集成了蓝牙4.2/5.0WiFi无线协议&#xff0c;采用双网口设计&#xff0c;1台主蓝牙网关可级联多个从蓝牙网关设备&#xff0c;至多支持远距离连接和控制38台蓝牙设备的蓝牙网关VDB3601&#xff0c;支持双蓝牙模组、485通信、可兼容4G/Cat.1模块&a…