Spring data jpa 外掛開發——解決規範和效率問題

廣州蘆葦科技Java開發團隊發表於2019-03-11

前言

一直苦於團隊的成員不按既定的規範來編寫程式碼以及使用JPA建立完實體後,還得建立對應的dao,service,controller、DTO,VO等等,所以就開發出了這款根據entity來自動生成這些既定的類以及對應的CRUD方法。好了,不多說了,直接進入主題吧!

idea建立MOJO專案

至於idea什麼建立MOJO專案,相信大家並不陌生,我這裡就不一一說明了。如果不會的朋友可以參考這篇文章

首先來看一下專案的結構

專案結構

其中:Mojo是外掛執行時的入口,Generator是用來生成對應的類,resources目錄存放對應的類别範本。 好了,接下來將給大家一一說明實現的具體細節。

DAO的生成

  • GenerateDaoMojo.java
package com.luwei.maven.jpa;

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.project.MavenProject;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description jpa外掛-生成dao
 * @Author LW-mochengdong
 * @Date 2019/2/19 15:43
 */
@Mojo(name = "gen-dao")
public class GenerateDaoMojo extends AbstractMojo {

    @Component
    private MavenProject mavenProject;

    @Override
    public void execute() {
        getLog().info("-----------------------start create dao---------------------------------");
        final String packagePath = "/src/main/java/com/luwei/models/".replace('/', File.separatorChar);
        String daoDir = mavenProject.getBasedir().getPath() + packagePath;
        new DaoGenerator(daoDir);
    }

}


複製程式碼
  • 說明

    • 外掛類必須繼承AbstractMojo抽象類,並且重寫其execute方法,execute方法是外掛執行的唯一入口
    • @Mojo(name = "gen-dao") 其中,gen-dao就是改外掛的名稱,引入到專案中時呈現的效果如圖所示:

    使用效果

    • MavenProject是引入外掛的專案本身,比如如果專案 project 映入了 該外掛,那麼mavenProject物件就是project,通過mavenProjec我們可以得到專案的所有資源。

    • daoDir就是專案的entity所在的路徑

  • DaoGenerator.java

package com.luwei.maven.jpa;

import org.stringtemplate.v4.ST;

import java.io.File;
import java.io.FileWriter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @Description
 * @Author LW-mochengdong
 * @Date 2019/2/19 17:27
 */
class DaoGenerator {

    DaoGenerator(String daoDir) {
        createDao(daoDir);
    }

    /**
     * @Description 根據entity所在的路徑,遞迴生成對應的DAO
     * @param filepath entity所在的路徑
     * @Return void
     * @Author LW-mochengdong
     * @Date 2019/3/7 20:17
     */
    private void createDao(String filepath) {
        File file = new File(filepath);
        if (file.exists()) {
            List<File> listFiles = Arrays.asList(Objects.requireNonNull(file.listFiles()));
            if (0 != listFiles.size()) {
                for (File f : listFiles) {//注意這裡不要用這種listFiles.forEach(file -> {});方式代替,否則會出現異常,具體原因現在未清楚
                    String absolutePath = f.getAbsolutePath();
                    if (f.isDirectory()) {//如果是路徑就遞迴呼叫,直到是檔案
                        createDao(absolutePath);
                    } else {
                        String entityName = f.getName();
                        if (!entityName.contains("Dao")) {//避免生成的DAO也會生DAO,這裡我們只需對entity生成對應的DAO即可
                            System.out.println(absolutePath + "=====" + entityName);
                            try {
                                String entity = entityName.substring(0, entityName.indexOf("."));//獲取entity名稱
                                String table = Utils.buildTable(entity);//獲取entity對應的表名稱
                                //獲取entity對應的DAO
                                File file1 = new File(absolutePath.substring(0,
                                        absolutePath.lastIndexOf(File.separatorChar)) + File.separatorChar + entity + "Dao.java");
                                if (!file1.exists()) {//如果DAO不存在則建立,避免覆蓋
                                    //讀取DAO模板
                                    ST st = new ST(Utils.getTemplate("/Dao.java"), '$', '$');
                                    //獲取entity所在的子路徑(包名的一部分)
                                    String childPackage = absolutePath.substring(absolutePath.indexOf("models"),
                                            absolutePath.lastIndexOf(File.separatorChar)).replace(String.valueOf(File.separatorChar), ".");
                                    //替換DAO模板中的佔位符
                                    st.add("childPackage", childPackage);
                                    st.add("entity", entity);
                                    st.add("table", table);
                                    String content = st.render();
                                    //建立檔案
                                    file1.createNewFile();
                                    //將檔案寫指定位置
                                    FileWriter writer = new FileWriter(file1);
                                    writer.write(content);
                                    writer.flush();
                                    writer.close();
                                    System.out.println(file1.getPath());
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }
    }

}


複製程式碼
  • 說明

    • createDao方法的功能是根據filepath(entity所在路徑)來找到所有的entity從而生成對應的DAO
    • 遍歷檔案時不要以java 8提供的forEach方式來遍歷,否則構建時會報異常具體原因還未知曉,有興趣的朋友可以發表一下寶貴觀點
    • 如果當前遍歷的是資料夾就需要呼叫遞迴呼叫方法本身,直到當前遍歷的是檔案才進行生產DAO邏輯
    • 判斷當前的檔案是否是外掛生成的檔案,如果是就忽略,否則生產的DAO還會生產於它對應的DAO
    • 生成DAO前要判斷是否存在以免覆蓋
  • Utils.java

package com.luwei.maven.jpa;

import org.codehaus.plexus.util.IOUtil;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;

/**
 * @Description
 * @Return
 * @Author LW-mochengdong
 * @Date 2019/2/19 17:02
 */
class Utils {

    static String getTemplate(String templateName) {
        InputStream templateInputStream = Utils.class.getResourceAsStream(templateName);
        StringWriter stringWriter = new StringWriter();
        try {
            IOUtil.copy(templateInputStream, stringWriter, "utf-8");
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return stringWriter.toString();
    }

    static String buildTable(String entity) {
        char[] chars = entity.toCharArray();
        StringBuilder stringBuilder = new StringBuilder();
        for (char c : chars) {
            if (Character.isUpperCase(c)) {
                stringBuilder.append("_").append(Character.toLowerCase(c));
            } else {
                stringBuilder.append(c);
            }
        }
        return stringBuilder.toString().substring(1);
    }

}

複製程式碼
  • Dao.java
package com.luwei.services.$childPackage$;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Set;

public interface $entity$Dao extends JpaRepository<$entity$, Integer>, JpaSpecificationExecutor<$entity$> {

    @Modifying
    @Query(value = "UPDATE tb_$table$ set deleted = true where deleted <> true AND $table$_id IN ?1", nativeQuery = true)
    int bathDeleted(@Param("ids") Set<Integer> ids);

}

複製程式碼

以上,entity的Dao生成已經完成,接下來我們只需要釋出到我們的私服即可。

  • 測試

    • 專案中引入外掛

      <plugin>
              <groupId>com.luwei</groupId>
              <artifactId>jpa-maven-plugin</artifactId>
              <version>1.0</version>
              <executions>
                  <execution>
                      <phase>compile</phase>
                      <goals>
                          <goal>gen-service</goal>
                      </goals>
                  </execution>
              </executions>
         </plugin>
      
      複製程式碼
    • 執行外掛,點選jpa:gen-dao後生成的效果如下

    執行效果

至此,根據entity生成對應Dao外掛已經開發完成,至於service、controller、DTO、VO的生成也是一樣的道理。如果有問題或疑問,歡迎大家在下方評論點贊,感謝您的支援!

成東

廣州蘆葦科技Java開發團隊

蘆葦科技-廣州專業網際網路軟體服務公司

抓住每一處細節 ,創造每一個美好

關注我們的公眾號,瞭解更多

想和我們一起奮鬥嗎?lagou搜尋“ 蘆葦科技 ”或者投放簡歷到 server@talkmoney.cn 加入我們吧

Spring data jpa 外掛開發——解決規範和效率問題

關注我們,你的評論和點贊對我們最大的支援

相關文章