title: 还在为效率以及规范烦恼的——这篇文章给你解决 tags: Spring boot,DATA JPA插件开发,maven,idea grammar_cjkRuby: true
Spring data jpa 插件开发——解决规范和效率问题
前言
一直苦于团队的成员不按既定的规范来编写代码以及使用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的生成也是一样的道理。如果有问题或疑问,欢迎大家在下方评论点赞,感谢您的支持!



