mybatis-generator生成報錯The specified target project directory xxx does not exist及源碼分析


mybatis-generator生成報錯The specified target project directory xxx does not exist及源碼分析

背景

參考macro大神的mall-learning項目搭建初始化框架的時候,在自己的項目里搭建了一個module來寫代碼,搭建generator代碼的時候報錯,The specified target project directory xxx does not exist
在這里插入圖片描述
參考了一篇博客
mybatis逆向工程 The specified target project directory src\main\resources does not exist

然后加上模塊名還是不對,還是報相同的錯誤

問題分析

這個報錯的意思是指定的包不存在
百度了一下配置文件里targetProject的含義:

 targetProject:目標項目,指定一個存在的目錄下,生成的內容會放到指定目錄中,如果目錄不存在,MBG不會自動建目錄

但是文章中有句話給了我提示,targetProject是相對路徑,為了搞清楚,我們看源碼

實現類Generator

public class Generator {
    public static void main(String[] args) throws Exception {
        //MBG 執行過程中的警告信息
        List<String> warnings = new ArrayList<String>();
        //當生成的代碼重復時,覆蓋原代碼
        boolean overwrite = true;
        //讀取我們的 MBG 配置文件
        InputStream is = Generator.class.getResourceAsStream("/generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(is);
        is.close();

        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        //創建 MBG
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        //執行生成代碼
        myBatisGenerator.generate(null);
        //輸出警告信息
        for (String warning : warnings) {
            System.out.println(warning);
        }
    }
}

異常是myBatisGenerator.generate(null);這句產生的,往下找

    /**
     * This is the main method for generating code. This method is long running, but progress can be provided and the
     * method can be cancelled through the ProgressCallback interface.
     *
     * @param callback
     *            an instance of the ProgressCallback interface, or <code>null</code> if you do not require progress
     *            information
     * @param contextIds
     *            a set of Strings containing context ids to run. Only the contexts with an id specified in this list
     *            will be run. If the list is null or empty, than all contexts are run.
     * @param fullyQualifiedTableNames
     *            a set of table names to generate. The elements of the set must be Strings that exactly match what's
     *            specified in the configuration. For example, if table name = "foo" and schema = "bar", then the fully
     *            qualified table name is "foo.bar". If the Set is null or empty, then all tables in the configuration
     *            will be used for code generation.
     * @param writeFiles
     *            if true, then the generated files will be written to disk.  If false,
     *            then the generator runs but nothing is written
     * @throws SQLException
     *             the SQL exception
     * @throws IOException
     *             Signals that an I/O exception has occurred.
     * @throws InterruptedException
     *             if the method is canceled through the ProgressCallback
     */
    public void generate(ProgressCallback callback, Set<String> contextIds,
            Set<String> fullyQualifiedTableNames, boolean writeFiles) throws SQLException,
            IOException, InterruptedException {

        if (callback == null) {
            callback = new NullProgressCallback();
        }

        generatedJavaFiles.clear();
        generatedXmlFiles.clear();

        // calculate the contexts to run
        List<Context> contextsToRun;
        if (contextIds == null || contextIds.size() == 0) {
            contextsToRun = configuration.getContexts();
        } else {
            contextsToRun = new ArrayList<Context>();
            for (Context context : configuration.getContexts()) {
                if (contextIds.contains(context.getId())) {
                    contextsToRun.add(context);
                }
            }
        }

        // setup custom classloader if required
        if (configuration.getClassPathEntries().size() > 0) {
            ClassLoader classLoader = getCustomClassloader(configuration.getClassPathEntries());
            ObjectFactory.addExternalClassLoader(classLoader);
        }

        // now run the introspections...
        int totalSteps = 0;
        for (Context context : contextsToRun) {
            totalSteps += context.getIntrospectionSteps();
        }
        callback.introspectionStarted(totalSteps);

        for (Context context : contextsToRun) {
            context.introspectTables(callback, warnings,
                    fullyQualifiedTableNames);
        }

        // now run the generates
        totalSteps = 0;
        for (Context context : contextsToRun) {
            totalSteps += context.getGenerationSteps();
        }
        callback.generationStarted(totalSteps);

        for (Context context : contextsToRun) {
            context.generateFiles(callback, generatedJavaFiles,
                    generatedXmlFiles, warnings);
        }

        // now save the files
        if (writeFiles) {
            callback.saveStarted(generatedXmlFiles.size()
                + generatedJavaFiles.size());

            for (GeneratedXmlFile gxf : generatedXmlFiles) {
                projects.add(gxf.getTargetProject());
                writeGeneratedXmlFile(gxf, callback);
            }

            for (GeneratedJavaFile gjf : generatedJavaFiles) {
                projects.add(gjf.getTargetProject());
                writeGeneratedJavaFile(gjf, callback);
            }

            for (String project : projects) {
                shellCallback.refreshProject(project);
            }
        }

        callback.done();
    }

打斷點發現主要是這里使用了配置文件里的target

        for (GeneratedJavaFile gjf : generatedJavaFiles) {
                projects.add(gjf.getTargetProject());
                writeGeneratedJavaFile(gjf, callback);
            }

我們進去查看這個方法的實現

private void writeGeneratedJavaFile(GeneratedJavaFile gjf, ProgressCallback callback)
           throws InterruptedException, IOException {
       File targetFile;
       String source;
       try {
           File directory = shellCallback.getDirectory(gjf
                   .getTargetProject(), gjf.getTargetPackage());
           targetFile = new File(directory, gjf.getFileName());
           if (targetFile.exists()) {
               if (shellCallback.isMergeSupported()) {
                   source = shellCallback.mergeJavaFile(gjf
                           .getFormattedContent(), targetFile
                           .getAbsolutePath(),
                           MergeConstants.OLD_ELEMENT_TAGS,
                           gjf.getFileEncoding());
               } else if (shellCallback.isOverwriteEnabled()) {
                   source = gjf.getFormattedContent();
                   warnings.add(getString("Warning.11", //$NON-NLS-1$
                           targetFile.getAbsolutePath()));
               } else {
                   source = gjf.getFormattedContent();
                   targetFile = getUniqueFileName(directory, gjf
                           .getFileName());
                   warnings.add(getString(
                           "Warning.2", targetFile.getAbsolutePath())); //$NON-NLS-1$
               }
           } else {
               source = gjf.getFormattedContent();
           }

           callback.checkCancel();
           callback.startTask(getString(
                   "Progress.15", targetFile.getName())); //$NON-NLS-1$
           writeFile(targetFile, source, gjf.getFileEncoding());
       } catch (ShellException e) {
           warnings.add(e.getMessage());
       }
   }

斷點發現主要是 File directory = shellCallback.getDirectory(gjf .getTargetProject(), gjf.getTargetPackage());拋出的異常

我們繼續往里面看

public File getDirectory(String targetProject, String targetPackage)
            throws ShellException {
        // targetProject is interpreted as a directory that must exist
        //
        // targetPackage is interpreted as a sub directory, but in package
        // format (with dots instead of slashes). The sub directory will be
        // created
        // if it does not already exist

        File project = new File(targetProject);
        if (!project.isDirectory()) {
            throw new ShellException(getString("Warning.9", //$NON-NLS-1$
                    targetProject));
        }

        StringBuilder sb = new StringBuilder();
        StringTokenizer st = new StringTokenizer(targetPackage, "."); //$NON-NLS-1$
        while (st.hasMoreTokens()) {
            sb.append(st.nextToken());
            sb.append(File.separatorChar);
        }

        File directory = new File(project, sb.toString());
        if (!directory.isDirectory()) {
            boolean rc = directory.mkdirs();
            if (!rc) {
                throw new ShellException(getString("Warning.10", //$NON-NLS-1$
                        directory.getAbsolutePath()));
            }
        }

        return directory;
    }

這里發現,我們generatorConfig.xmltargetProject主要是在這個方法里生成File對象
又因為File創建可以根據絕對路徑,也可以根據相對路徑,當我們在generatorConfig.xml配置相對路徑的時候,File對象會以項目路徑(即user.dir目錄)去尋找這個FILE對象,由於相對路徑不對,找不到對應的文件夾,project.isDirectory()就為false,就會爆出The specified target project directory xxx does not exist的異常,也因為這個問題,我們就能理解這句話 targetProject:目標項目,指定一個存在的目錄下,生成的內容會放到指定目錄中,如果目錄不存在,MBG不會自動建目錄的意義,當文件夾不存在的時候,他會拋出異常,而不會創建相關文件

解決方法

  • 使用絕對路徑
  • 使用相對路徑的時候找准相對路徑
    • 介紹一個簡單的找相對路徑方法,idea左側目錄找到你的module,右鍵,copy relative path就可以得到這個module相對於usr.dir的相對路徑,如(module-one),然后拼接原來的src/main/java,組成module-one/src/main/java即可
      在這里插入圖片描述

參考文章


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM