• SpringBoot系列——Spring-Data-JPA(究极进化版) 自动生成单表基础增、删、改、查接口


      前言

      我们在之前的实现了springboot与data-jpa的增、删、改、查简单使用(请戳:SpringBoot系列——Spring-Data-JPA),并实现了升级版(请戳:SpringBoot系列——Spring-Data-JPA(升级版)),在基础版、升级版中,我们实现了单表的基础get、save(插入/更新)、list、page、delete接口,并写了一套通用common代码,每个单表去继承从而实现这套基础接口、同时,我们使用用Vo去接收、传输数据,实体负责与数据库表映射。

      但是,单表增、删、改、查基础功能相似,代码高度相似,我们新增一个单表操作的步骤:复制、粘贴、修改文件夹、文件名、类名、修改传参实体对象...,为了实现快速开发,我们在前面两个版本的基础上,使用代码自动生成一套单表的基础增、删、改、查接口

       代码编写

      首先我们先创建一个新的工程项目,实现一套common代码,以便单表直接继承

      CodeDOM.java是一个比较大的类,因为我把所有的东西都丢在了里面...,接下来我们拆分讲解一下步骤(文末贴出CodeDOM.java完整代码)

      首先定义几个属性,构造函数传入表名,基础路径基于表名进行赋值

        /**
         * 表名
         */
        private String tableName;
    
        /**
         * 基础路径
         */
        private String basePackage_;
        private String package_;
        private String basePath;

      构造函数

        /**
         * 构造参数,出入表名
         */
        private CodeDOM(String tableName) {
            this.tableName = tableName;
            basePackage_ = "cn\huanzi\springbootjpa\";
            package_ = basePackage_ + StringUtil.camelCaseName(tableName).toLowerCase() + "\";
            //System.getProperty("user.dir") 获取的是项目所在路径,如果我们是子项目,则需要添加一层路径
            basePath = System.getProperty("user.dir") + "\src\main\java\" + package_;
        }

      查询表信息

      首先要连接数据库,连接数据使用jdbc进行连接

        /**
         * 数据连接相关
         */
        private static final String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8";
        private static final String USERNAME = "root";
        private static final String PASSWORD = "123456";
        private static final String DRIVERCLASSNAME = "com.mysql.jdbc.Driver";
        /**
         * JDBC连接数据库工具类
         */
        private static class DBConnectionUtil {
    
            static {
                // 1、加载驱动
                try {
                    Class.forName(DRIVERCLASSNAME);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
    
            /**
             * 返回一个Connection连接
             *
             * @return
             */
            public static Connection getConnection() {
                Connection conn = null;
                // 2、连接数据库
                try {
                    conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                return conn;
            }
    
            /**
             * 关闭Connection,Statement连接
             *
             * @param conn
             * @param stmt
             */
            public static void close(Connection conn, Statement stmt) {
                try {
                    conn.close();
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            /**
             * 关闭Connection,Statement,ResultSet连接
             *
             * @param conn
             * @param stmt
             * @param rs
             */
            public static void close(Connection conn, Statement stmt, ResultSet rs) {
                try {
                    close(conn, stmt);
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
        }

      获取数据表的表机构信息(本系列用的是MySQL数据库),通过查询 information_schema 表,SQL:

    select column_name,data_type,column_comment,column_key,extra from information_schema.columns where table_name=?

      创建一个表结构对象,然后封装一下方法:

        /**
         * 表结构行信息实体类
         */
        private class TableInfo {
            private String columnName;
            private String dataType;
            private String columnComment;
            private String columnKey;
            private String extra;
    
            TableInfo() {
            }
    
            String getColumnName() {
                return columnName;
            }
    
            void setColumnName(String columnName) {
                this.columnName = columnName;
            }
    
            String getDataType() {
                return dataType;
            }
    
            void setDataType(String dataType) {
                this.dataType = dataType;
            }
    
            String getColumnComment() {
                return columnComment;
            }
    
            void setColumnComment(String columnComment) {
                this.columnComment = columnComment;
            }
    
            String getColumnKey() {
                return columnKey;
            }
    
            void setColumnKey(String columnKey) {
                this.columnKey = columnKey;
            }
    
            String getExtra() {
                return extra;
            }
    
            void setExtra(String extra) {
                this.extra = extra;
            }
        }
        /**
         * 获取表结构信息
         *
         * @return list
         */
        private List<TableInfo> getTableInfo() {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            ArrayList<TableInfo> list = new ArrayList<>();
            try {
                conn = DBConnectionUtil.getConnection();
                String sql = "select column_name,data_type,column_comment,column_key,extra from information_schema.columns where table_name=?";
                ps = conn.prepareStatement(sql);
                ps.setString(1, tableName);
                rs = ps.executeQuery();
                while (rs.next()) {
                    TableInfo tableInfo = new TableInfo();
                    //列名,全部转为小写
                    tableInfo.setColumnName(rs.getString("column_name").toLowerCase());
                    //列类型
                    tableInfo.setDataType(rs.getString("data_type"));
                    //列注释
                    tableInfo.setColumnComment(rs.getString("column_comment"));
                    //主键
                    tableInfo.setColumnKey(rs.getString("column_key"));
                    //主键类型
                    tableInfo.setExtra(rs.getString("extra"));
                    list.add(tableInfo);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                assert rs != null;
                DBConnectionUtil.close(conn, ps, rs);
            }
            return list;
        }

      那么oracle应该怎么查呢?

    --表结构信息
    select * from user_tab_columns where table_name='TB_USER'
    --表字段注释
    select * from user_col_comments  where table_name='TB_USER'
    --表注释
    select * from user_tab_comments  where table_name='TB_USER'

      我们看一下系统都提供有哪些表,这...太多了

      数据处理

      数据处理,比如数据字段类型转成java类型、字段的下划线转驼峰等,我们封装一下工具类:

        /**
         * 字符串处理工具类
         */
        private static class StringUtil {
            /**
             * 数据库类型->JAVA类型
             *
             * @param dbType 数据库类型
             * @return JAVA类型
             */
            private static String typeMapping(String dbType) {
                String javaType = "";
                if ("int|integer".contains(dbType)) {
                    javaType = "Integer";
                } else if ("float|double|decimal|real".contains(dbType)) {
                    javaType = "Double";
                } else if ("date|time|datetime|timestamp".contains(dbType)) {
                    javaType = "Date";
                } else {
                    javaType = "String";
                }
                return javaType;
            }
    
            /**
             * 驼峰转换为下划线
             */
            public static String underscoreName(String camelCaseName) {
                StringBuilder result = new StringBuilder();
                if (camelCaseName != null && camelCaseName.length() > 0) {
                    result.append(camelCaseName.substring(0, 1).toLowerCase());
                    for (int i = 1; i < camelCaseName.length(); i++) {
                        char ch = camelCaseName.charAt(i);
                        if (Character.isUpperCase(ch)) {
                            result.append("_");
                            result.append(Character.toLowerCase(ch));
                        } else {
                            result.append(ch);
                        }
                    }
                }
                return result.toString();
            }
    
            /**
             * 首字母大写
             */
            public static String captureName(String name) {
                char[] cs = name.toCharArray();
                cs[0] -= 32;
                return String.valueOf(cs);
    
            }
    
            /**
             * 下划线转换为驼峰
             */
            public static String camelCaseName(String underscoreName) {
                StringBuilder result = new StringBuilder();
                if (underscoreName != null && underscoreName.length() > 0) {
                    boolean flag = false;
                    for (int i = 0; i < underscoreName.length(); i++) {
                        char ch = underscoreName.charAt(i);
                        if ("_".charAt(0) == ch) {
                            flag = true;
                        } else {
                            if (flag) {
                                result.append(Character.toUpperCase(ch));
                                flag = false;
                            } else {
                                result.append(ch);
                            }
                        }
                    }
                }
                return result.toString();
            }
        }

      文件处理

      文件处理比如创建文件夹、文件,将字符写入文件等,我们先获取一下基础路径,并封装一下文件工具类:

        /**
         * file工具类
         */
        private static class FileUtil {
            /**
             * 创建文件
             *
             * @param pathNameAndFileName 路径跟文件名
             * @return File对象
             */
            private static File createFile(String pathNameAndFileName) {
                File file = new File(pathNameAndFileName);
                try {
                    //获取父目录
                    File fileParent = file.getParentFile();
                    if (!fileParent.exists()) {
                        fileParent.mkdirs();
                    }
                    //创建文件
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                } catch (Exception e) {
                    file = null;
                    System.err.println("新建文件操作出错");
                    e.printStackTrace();
                }
                return file;
            }
    
            /**
             * 字符流写入文件
             *
             * @param file         file对象
             * @param stringBuffer 要写入的数据
             */
            private static void fileWriter(File file, StringBuffer stringBuffer) {
                //字符流
                try {
                    FileWriter resultFile = new FileWriter(file, true);//true,则追加写入 false,则覆盖写入
                    PrintWriter myFile = new PrintWriter(resultFile);
                    //写入
                    myFile.println(stringBuffer.toString());
    
                    myFile.close();
                    resultFile.close();
                } catch (Exception e) {
                    System.err.println("写入操作出错");
                    e.printStackTrace();
                }
            }
        }

      

      创建代码

      根据我们项目的路径规范,代码编写规范,我们定义好文件的模板,并封装成对应的方法

        /**
         * 创建pojo实体类
         */
        private void createPojo(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "pojo\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ".java");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "pojo;
    " +
                            "
    " +
                            "import lombok.Data;
    " +
                            "import javax.persistence.*;
    " +
                            "import java.io.Serializable;
    " +
                            "import java.util.Date;
    " +
                            "
    " +
                            "@Entity
    " +
                            "@Table(name = "" + tableName + "")
    " +
                            "@Data
    " +
                            "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + " implements Serializable {
    "
            );
            //遍历设置属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    stringBuffer.append("    @Id
    ");
                }
                //自增
                if ("auto_increment".equals(tableInfo.getExtra())) {
                    stringBuffer.append("    @GeneratedValue(strategy= GenerationType.IDENTITY)
    ");
                }
                stringBuffer.append("    private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "
    
    ");
            }
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
        }
        /**
         * 创建vo类
         */
        private void createVo(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "vo\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo.java");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "vo;
    " +
                            "
    " +
    "import "+ basePackage_.replaceAll("\\", ".") +" common.pojo.PageCondition;"+ "import lombok.Data; " + "import java.io.Serializable; " + "import java.util.Date; " + " " + "@Data " + "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo extends PageCondition implements Serializable { " ); //遍历设置属性 for (TableInfo tableInfo : tableInfos) { stringBuffer.append(" private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + " "); } stringBuffer.append("}"); FileUtil.fileWriter(file, stringBuffer); }
        /**
         * 创建repository类
         */
        private void createRepository(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "repository\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository.java");
            StringBuffer stringBuffer = new StringBuffer();
            String t = "String";
            //遍历属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    t = StringUtil.typeMapping(tableInfo.getDataType());
                }
            }
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "repository;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.repository.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import org.springframework.stereotype.Repository;
    " +
                            "
    " +
                            "@Repository
    " +
                            "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository extends CommonRepository<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
            );
            stringBuffer.append("
    ");
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
        }
        /**
         * 创建service类
         */
        private void createService(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "service\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service.java");
            StringBuffer stringBuffer = new StringBuffer();
            String t = "String";
            //遍历属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    t = StringUtil.typeMapping(tableInfo.getDataType());
                }
            }
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "service;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.service.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import " + package_.replaceAll("\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;
    " +
                            "
    " +
                            "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service extends CommonService<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
            );
            stringBuffer.append("
    ");
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
    
            //Impl
            File file1 = FileUtil.createFile(basePath + "service\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl.java");
            StringBuffer stringBuffer1 = new StringBuffer();
            stringBuffer1.append(
                    "package " + package_.replaceAll("\\", ".") + "service;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.service.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import " + package_.replaceAll("\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;
    " +
                            "import " + package_.replaceAll("\\", ".") + "repository." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository;
    " +
                            "import org.springframework.beans.factory.annotation.Autowired;
    " +
                            "import org.springframework.stereotype.Service;
    " +
                            "import org.springframework.transaction.annotation.Transactional;
    " +
                            "import javax.persistence.EntityManager;
    " +
                            "import javax.persistence.PersistenceContext;
    " +
                            "
    " +
                            "@Service
    " +
                            "@Transactional
    " +
                            "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl extends CommonServiceImpl<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> implements " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service{"
            );
            stringBuffer1.append("
    
    ");
            stringBuffer1.append(
                    "    @PersistenceContext
    " +
                            "    private EntityManager em;
    ");
    
            stringBuffer1.append("" +
                    "    @Autowired
    " +
                    "    private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository " + StringUtil.camelCaseName(tableName) + "Repository;
    ");
            stringBuffer1.append("}");
            FileUtil.fileWriter(file1, stringBuffer1);
        }
        /**
         * 创建controller类
         */
        private void createController(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "controller\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller.java");
            StringBuffer stringBuffer = new StringBuffer();
            String t = "String";
            //遍历属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    t = StringUtil.typeMapping(tableInfo.getDataType());
                }
            }
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "controller;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.controller.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import " + package_.replaceAll("\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;
    " +
                            "import " + package_.replaceAll("\\", ".") + "service." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service;
    " +
                            "import org.springframework.beans.factory.annotation.Autowired;
    " +
                            "import org.springframework.web.bind.annotation.*;
    " +
                            "
    " +
                            "@RestController
    " +
                            "@RequestMapping("/" + StringUtil.camelCaseName(tableName) + "/")
    " +
                            "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller extends CommonController<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
            );
            stringBuffer.append("
    ");
            stringBuffer.append("" +
                    "    @Autowired
    " +
                    "    private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service " + StringUtil.camelCaseName(tableName) + "Service;
    ");
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
        }

       需要注意的是:目前生成的pojo的主键,只用了@Id声明该属性为表主键,尚缺一个主键生成策略,这个需要根据自己的情况来选择主键生成策略

      PS:缺少主键生成策略或者设置错误将会出现以下问题:

      程序不报错,JPA查询出来的数据长度正常,内容都是重复的,但mysql数据库运行查询语句结果正常

        /*
            JPA提供的四种主键生成策略
            GenerationType.TABLE:使用一个特定的数据库表格来保存主键。 
            GenerationType.SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。 
            GenerationType.IDENTITY:主键由数据库自动生成(主要是自动增长型) 
            GenerationType.AUTO:主键由程序控制。
         */
        @GeneratedValue(strategy = GenerationType.IDENTITY)

      提供一个方法让外部直接调用

        /**
         * 快速创建,供外部调用,调用之前先设置一下项目的基础路径
         */
        private String create() {
            List<TableInfo> tableInfo = getTableInfo();
            createPojo(tableInfo);
            createVo(tableInfo);
            createRepository(tableInfo);
            createService(tableInfo);
            createController(tableInfo);
            return tableName + " 后台代码生成完毕!";
        }

      效果演示

      main方法运行

        public static void main(String[] args) {
            String[] tables = {"tb_user"};
            for (int i = 0; i < tables.length; i++) {
                String msg = new CodeDOM(tables[i]).create();
                System.out.println(msg);
            }
        }

       生成代码

      我们查看一下生成的代码

    package cn.huanzi.springbootjpa.tbuser.pojo;
    
    import lombok.Data;
    import javax.persistence.*;
    import java.io.Serializable;
    import java.util.Date;
    
    @Entity
    @Table(name = "tb_user")
    @Data
    public class TbUser implements Serializable {
        @Id
        @GeneratedValue(strategy= GenerationType.IDENTITY)
        private Integer id;//表id
    
        private String username;//用户名
    
        private String password;//密码
    
        private Date created;//创建时间
    
        private Integer descriptionId;//关联详情id
    
    }
    package cn.huanzi.springbootjpa.tbuser.vo;
    
    import lombok.Data;
    import java.io.Serializable;
    import java.util.Date;
    
    @Data
    public class TbUserVo extends PageCondition implements Serializable {
        private Integer id;//表id
    
        private String username;//用户名
    
        private String password;//密码
    
        private Date created;//创建时间
    
        private Integer descriptionId;//关联详情id
    
    }
    package cn.huanzi.springbootjpa.tbuser.repository;
    
    import cn.huanzi.springbootjpa.common.repository.*;
    import cn.huanzi.springbootjpa.tbuser.pojo.TbUser;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public interface TbUserRepository extends CommonRepository<TbUser, Integer> {
    }
    package cn.huanzi.springbootjpa.tbuser.service;
    
    import cn.huanzi.springbootjpa.common.service.*;
    import cn.huanzi.springbootjpa.tbuser.pojo.TbUser;
    import cn.huanzi.springbootjpa.tbuser.vo.TbUserVo;
    
    public interface TbUserService extends CommonService<TbUserVo, TbUser, Integer> {
    }
    package cn.huanzi.springbootjpa.tbuser.service;
    
    import cn.huanzi.springbootjpa.common.service.*;
    import cn.huanzi.springbootjpa.tbuser.pojo.TbUser;
    import cn.huanzi.springbootjpa.tbuser.vo.TbUserVo;
    import cn.huanzi.springbootjpa.tbuser.repository.TbUserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    import javax.persistence.EntityManager;
    import javax.persistence.PersistenceContext;
    
    @Service
    @Transactional
    public class TbUserServiceImpl extends CommonServiceImpl<TbUserVo, TbUser, Integer> implements TbUserService{
    
        @PersistenceContext
        private EntityManager em;
        @Autowired
        private TbUserRepository tbUserRepository;
    }
    package cn.huanzi.springbootjpa.tbuser.controller;
    
    import cn.huanzi.springbootjpa.common.controller.*;
    import cn.huanzi.springbootjpa.tbuser.pojo.TbUser;
    import cn.huanzi.springbootjpa.tbuser.vo.TbUserVo;
    import cn.huanzi.springbootjpa.tbuser.service.TbUserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/tbUser/")
    public class TbUserController extends CommonController<TbUserVo, TbUser, Integer> {
        @Autowired
        private TbUserService tbUserService;
    }

      我们启动项目

      依次访问基础接口:

      get接口

      http://localhost:10086/tbUser/get/1

     

      page接口

      http://localhost:10086/tbUser/page?page=1&rows=10

      

       list接口

      http://localhost:10086/tbUser/list

     

      save接口

      (插入跟更新)

      没有id或id不存在,为插入,http://localhost:10086/tbUser/save?username=张麻子&password=123

      id已存在,则为更新,注意:这里的更新是你的字段是什么jpa就帮你存什么,如果想要实现只更新接参对象有值的字段,应该先用id去同步数据,再更新,

     

      delete接口

      http://localhost:10086/tbUser/delete/15

      扩展

      1、有一些同学会发现,代码生成后idea并没有帮我们扫描出来,这时候我们可以手动去刷新一下,对着我们的项目右键,然后刷新

      2、个人觉得代码生成用groovy更加合适,只是我现在对它的语法使用还不熟悉,后面我们可以尝试一下使用groovy来生成代码,在idea里使用groovy生成代码:

      groovy文件的位置:

      使用方法:用idea的datebase连接数据库后,对着表右击

      后记

      这套代码的风格是单表继承通用CRUD、分页、排序接口,在启动类的同级目录下面,按一张表一个目录分层级存放文件,技术选型:springboot + thymeleaf + springdata-jpa + mysql,pojo实体对象负责ORM框架与数据库的映射,vo对象负责接参、传参,vo与pojo通过CopyUtil工具类进相互转换

      一人挖井,全村喝水;有了这一套基础代码跟代码自动生成单表基础增、删、改、查接口,我们的开发效率大大提高

      完整代码

    package cn.huanzi.qch.springbootjpa.util;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.io.PrintWriter;
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 自动生成代码
     */
    public class CodeDOM {
    
        /**
         * 构造参数,出入表名
         */
        private CodeDOM(String tableName) {
            this.tableName = tableName;
            basePackage_ = "cn\huanzi\qch\springbootjpa\";
            package_ = basePackage_ + StringUtil.camelCaseName(tableName).toLowerCase() + "\";
            //System.getProperty("user.dir") 获取的是项目所在路径,如果我们是子项目,则需要添加一层路径
            basePath = System.getProperty("user.dir") + "\springboot-jpa\src\main\java\" + package_;
        }
    
        /**
         * 数据连接相关
         */
        private static final String URL = "jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8&characterEncoding=utf-8";
        private static final String USERNAME = "root";
        private static final String PASSWORD = "123456";
        private static final String DRIVERCLASSNAME = "com.mysql.jdbc.Driver";
        /**
         * 表名
         */
        private String tableName;
    
        /**
         * 基础路径
         */
        private String basePackage_;
        private String package_;
        private String basePath;
    
        /**
         * 创建pojo实体类
         */
        private void createPojo(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "pojo\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ".java");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "pojo;
    " +
                            "
    " +
                            "import lombok.Data;
    " +
                            "import javax.persistence.*;
    " +
                            "import java.io.Serializable;
    " +
                            "import java.util.Date;
    " +
                            "
    " +
                            "@Entity
    " +
                            "@Table(name = "" + tableName + "")
    " +
                            "@Data
    " +
                            "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + " implements Serializable {
    "
            );
            //遍历设置属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    stringBuffer.append("    @Id
    ");
                }
                //自增
                if ("auto_increment".equals(tableInfo.getExtra())) {
                    stringBuffer.append("    @GeneratedValue(strategy= GenerationType.IDENTITY)
    ");
                }
                stringBuffer.append("    private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "
    
    ");
            }
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
        }
    
        /**
         * 创建vo类
         */
        private void createVo(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "vo\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo.java");
            StringBuffer stringBuffer = new StringBuffer();
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "vo;
    " +
                            "
    " +
                            "import "+ basePackage_.replaceAll("\\", ".") +" common.pojo.PageCondition;"+
                            "import lombok.Data;
    " +
                            "import java.io.Serializable;
    " +
                            "import java.util.Date;
    " +
                            "
    " +
                            "@Data
    " +
                            "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo extends PageCondition implements Serializable {
    "
            );
            //遍历设置属性
            for (TableInfo tableInfo : tableInfos) {
                stringBuffer.append("    private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "
    
    ");
            }
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
        }
    
        /**
         * 创建repository类
         */
        private void createRepository(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "repository\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository.java");
            StringBuffer stringBuffer = new StringBuffer();
            String t = "String";
            //遍历属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    t = StringUtil.typeMapping(tableInfo.getDataType());
                }
            }
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "repository;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.repository.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import org.springframework.stereotype.Repository;
    " +
                            "
    " +
                            "@Repository
    " +
                            "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository extends CommonRepository<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
            );
            stringBuffer.append("
    ");
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
        }
    
        /**
         * 创建service类
         */
        private void createService(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "service\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service.java");
            StringBuffer stringBuffer = new StringBuffer();
            String t = "String";
            //遍历属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    t = StringUtil.typeMapping(tableInfo.getDataType());
                }
            }
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "service;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.service.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import " + package_.replaceAll("\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;
    " +
                            "
    " +
                            "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service extends CommonService<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
            );
            stringBuffer.append("
    ");
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
    
            //Impl
            File file1 = FileUtil.createFile(basePath + "service\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl.java");
            StringBuffer stringBuffer1 = new StringBuffer();
            stringBuffer1.append(
                    "package " + package_.replaceAll("\\", ".") + "service;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.service.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import " + package_.replaceAll("\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;
    " +
                            "import " + package_.replaceAll("\\", ".") + "repository." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository;
    " +
                            "import org.springframework.beans.factory.annotation.Autowired;
    " +
                            "import org.springframework.stereotype.Service;
    " +
                            "import org.springframework.transaction.annotation.Transactional;
    " +
                            "import javax.persistence.EntityManager;
    " +
                            "import javax.persistence.PersistenceContext;
    " +
                            "
    " +
                            "@Service
    " +
                            "@Transactional
    " +
                            "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "ServiceImpl extends CommonServiceImpl<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> implements " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service{"
            );
            stringBuffer1.append("
    
    ");
            stringBuffer1.append(
                    "    @PersistenceContext
    " +
                            "    private EntityManager em;
    ");
    
            stringBuffer1.append("" +
                    "    @Autowired
    " +
                    "    private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository " + StringUtil.camelCaseName(tableName) + "Repository;
    ");
            stringBuffer1.append("}");
            FileUtil.fileWriter(file1, stringBuffer1);
        }
    
        /**
         * 创建controller类
         */
        private void createController(List<TableInfo> tableInfos) {
            File file = FileUtil.createFile(basePath + "controller\" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller.java");
            StringBuffer stringBuffer = new StringBuffer();
            String t = "String";
            //遍历属性
            for (TableInfo tableInfo : tableInfos) {
                //主键
                if ("PRI".equals(tableInfo.getColumnKey())) {
                    t = StringUtil.typeMapping(tableInfo.getDataType());
                }
            }
            stringBuffer.append(
                    "package " + package_.replaceAll("\\", ".") + "controller;
    " +
                            "
    " +
                            "import " + basePackage_.replaceAll("\\", ".") + "common.controller.*;
    " +
                            "import " + package_.replaceAll("\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";
    " +
                            "import " + package_.replaceAll("\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;
    " +
                            "import " + package_.replaceAll("\\", ".") + "service." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service;
    " +
                            "import org.springframework.beans.factory.annotation.Autowired;
    " +
                            "import org.springframework.web.bind.annotation.*;
    " +
                            "
    " +
                            "@RestController
    " +
                            "@RequestMapping("/" + StringUtil.camelCaseName(tableName) + "/")
    " +
                            "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller extends CommonController<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
            );
            stringBuffer.append("
    ");
            stringBuffer.append("" +
                    "    @Autowired
    " +
                    "    private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service " + StringUtil.camelCaseName(tableName) + "Service;
    ");
            stringBuffer.append("}");
            FileUtil.fileWriter(file, stringBuffer);
        }
    
        /**
         * 获取表结构信息
         *
         * @return list
         */
        private List<TableInfo> getTableInfo() {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            ArrayList<TableInfo> list = new ArrayList<>();
            try {
                conn = DBConnectionUtil.getConnection();
                String sql = "select column_name,data_type,column_comment,column_key,extra from information_schema.columns where table_name=?";
                ps = conn.prepareStatement(sql);
                ps.setString(1, tableName);
                rs = ps.executeQuery();
                while (rs.next()) {
                    TableInfo tableInfo = new TableInfo();
                    //列名,全部转为小写
                    tableInfo.setColumnName(rs.getString("column_name").toLowerCase());
                    //列类型
                    tableInfo.setDataType(rs.getString("data_type"));
                    //列注释
                    tableInfo.setColumnComment(rs.getString("column_comment"));
                    //主键
                    tableInfo.setColumnKey(rs.getString("column_key"));
                    //主键类型
                    tableInfo.setExtra(rs.getString("extra"));
                    list.add(tableInfo);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                assert rs != null;
                DBConnectionUtil.close(conn, ps, rs);
            }
            return list;
        }
    
        /**
         * file工具类
         */
        private static class FileUtil {
            /**
             * 创建文件
             *
             * @param pathNameAndFileName 路径跟文件名
             * @return File对象
             */
            private static File createFile(String pathNameAndFileName) {
                File file = new File(pathNameAndFileName);
                try {
                    //获取父目录
                    File fileParent = file.getParentFile();
                    if (!fileParent.exists()) {
                        fileParent.mkdirs();
                    }
                    //创建文件
                    if (!file.exists()) {
                        file.createNewFile();
                    }
                } catch (Exception e) {
                    file = null;
                    System.err.println("新建文件操作出错");
                    e.printStackTrace();
                }
                return file;
            }
    
            /**
             * 字符流写入文件
             *
             * @param file         file对象
             * @param stringBuffer 要写入的数据
             */
            private static void fileWriter(File file, StringBuffer stringBuffer) {
                //字符流
                try {
                    FileWriter resultFile = new FileWriter(file, false);//true,则追加写入 false,则覆盖写入
                    PrintWriter myFile = new PrintWriter(resultFile);
                    //写入
                    myFile.println(stringBuffer.toString());
    
                    myFile.close();
                    resultFile.close();
                } catch (Exception e) {
                    System.err.println("写入操作出错");
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 字符串处理工具类
         */
        private static class StringUtil {
            /**
             * 数据库类型->JAVA类型
             *
             * @param dbType 数据库类型
             * @return JAVA类型
             */
            private static String typeMapping(String dbType) {
                String javaType = "";
                if ("int|integer".contains(dbType)) {
                    javaType = "Integer";
                } else if ("float|double|decimal|real".contains(dbType)) {
                    javaType = "Double";
                } else if ("date|time|datetime|timestamp".contains(dbType)) {
                    javaType = "Date";
                } else {
                    javaType = "String";
                }
                return javaType;
            }
    
            /**
             * 驼峰转换为下划线
             */
            public static String underscoreName(String camelCaseName) {
                StringBuilder result = new StringBuilder();
                if (camelCaseName != null && camelCaseName.length() > 0) {
                    result.append(camelCaseName.substring(0, 1).toLowerCase());
                    for (int i = 1; i < camelCaseName.length(); i++) {
                        char ch = camelCaseName.charAt(i);
                        if (Character.isUpperCase(ch)) {
                            result.append("_");
                            result.append(Character.toLowerCase(ch));
                        } else {
                            result.append(ch);
                        }
                    }
                }
                return result.toString();
            }
    
            /**
             * 首字母大写
             */
            public static String captureName(String name) {
                char[] cs = name.toCharArray();
                cs[0] -= 32;
                return String.valueOf(cs);
    
            }
    
            /**
             * 下划线转换为驼峰
             */
            public static String camelCaseName(String underscoreName) {
                StringBuilder result = new StringBuilder();
                if (underscoreName != null && underscoreName.length() > 0) {
                    boolean flag = false;
                    for (int i = 0; i < underscoreName.length(); i++) {
                        char ch = underscoreName.charAt(i);
                        if ("_".charAt(0) == ch) {
                            flag = true;
                        } else {
                            if (flag) {
                                result.append(Character.toUpperCase(ch));
                                flag = false;
                            } else {
                                result.append(ch);
                            }
                        }
                    }
                }
                return result.toString();
            }
        }
    
        /**
         * JDBC连接数据库工具类
         */
        private static class DBConnectionUtil {
    
            {
                // 1、加载驱动
                try {
                    Class.forName(DRIVERCLASSNAME);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
    
            /**
             * 返回一个Connection连接
             *
             * @return
             */
            public static Connection getConnection() {
                Connection conn = null;
                // 2、连接数据库
                try {
                    conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                return conn;
            }
    
            /**
             * 关闭Connection,Statement连接
             *
             * @param conn
             * @param stmt
             */
            public static void close(Connection conn, Statement stmt) {
                try {
                    conn.close();
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            /**
             * 关闭Connection,Statement,ResultSet连接
             *
             * @param conn
             * @param stmt
             * @param rs
             */
            public static void close(Connection conn, Statement stmt, ResultSet rs) {
                try {
                    close(conn, stmt);
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
        /**
         * 表结构行信息实体类
         */
        private class TableInfo {
            private String columnName;
            private String dataType;
            private String columnComment;
            private String columnKey;
            private String extra;
    
            TableInfo() {
            }
    
            String getColumnName() {
                return columnName;
            }
    
            void setColumnName(String columnName) {
                this.columnName = columnName;
            }
    
            String getDataType() {
                return dataType;
            }
    
            void setDataType(String dataType) {
                this.dataType = dataType;
            }
    
            String getColumnComment() {
                return columnComment;
            }
    
            void setColumnComment(String columnComment) {
                this.columnComment = columnComment;
            }
    
            String getColumnKey() {
                return columnKey;
            }
    
            void setColumnKey(String columnKey) {
                this.columnKey = columnKey;
            }
    
            String getExtra() {
                return extra;
            }
    
            void setExtra(String extra) {
                this.extra = extra;
            }
        }
    
        /**
         * 快速创建,供外部调用,调用之前先设置一下项目的基础路径
         */
        private String create() {
            List<TableInfo> tableInfo = getTableInfo();
            createPojo(tableInfo);
            createVo(tableInfo);
            createRepository(tableInfo);
            createService(tableInfo);
            createController(tableInfo);
            System.out.println("生成路径位置:" + basePath);
            return tableName + " 后台代码生成完毕!";
        }
    
        public static void main(String[] args) {
            String[] tables = {"tb_description"};
            for (String table : tables) {
                String msg = new CodeDOM(table).create();
                System.out.println(msg);
            }
        }
    }
    CodeDOM

      补充

      1、发现了一个问题,我们在自动生成controller里有个地方是写死的...

      改一下,顺便升级一下CodeDOM类,我已经更新了博客文章,现在你看的文章已经是正确的,且是升级后的版本。 

      2、有细心园友发现我们漏贴了CommonController的代码,我们在这里补贴一下,另外说一下,其他的common代码在jpa升级版中 SpringBoot系列——Spring-Data-JPA(升级版),但里面当时我们只写了service层、repository层的通用代码,以及通讯对象和实体与Vo转换工具等其他公用代码,controller是在本文才加上去的。

    /**
     * 通用Controller
     *
     * @param <V> 实体类Vo
     * @param <E> 实体类
     * @param <T> id主键类型
     */
    public class CommonController<V, E,T> {
    
        @Autowired
        private CommonService<V, E,T> commonService;
        
        /*
            CRUD、分页、排序测试
         */
        //    @PostMapping("page")
        @RequestMapping("page")
        public Result<PageInfo<V>> page(V entityVo) {
            return commonService.page(entityVo);
        }
    
        //    @PostMapping("list")
        @RequestMapping("list")
        public Result<List<V>> list(V entityVo) {
            return commonService.list(entityVo);
        }
    
        //    @GetMapping("get/{id}")
        @RequestMapping("get/{id}")
        public Result<V> get( @PathVariable("id") T id) {
            return commonService.get(id);
        }
    
        //    @PostMapping("save")
        @RequestMapping("save")
        public Result<V> save( V entityVo) {
            return commonService.save(entityVo);
        }
    
        //    @GetMapping("delete/{id}")
        @RequestMapping("delete/{id}")
        public Result<T> delete( @PathVariable("id") T id) {
            /*
            批量删除
            @DeleteMapping("deleteBatch")
            public Result<T> deleteBatch(@RequestBody List<String> ids){}
            前端调用:
            $.ajax({
                url: ctx + "deleteBatch",
                type: "DELETE",
                data: JSON.stringify([id1,id2]),
                dataType: "JSON",
                contentType: 'application/json',
                success: function (data) { 
    
                }
            });
             */
            return commonService.delete(id);
        }
    }
    CommonController

      代码开源

      代码已经开源、托管到我的GitHub、码云:

      GitHub:https://github.com/huanzi-qch/springBoot

      码云:https://gitee.com/huanzi-qch/springBoot

  • 相关阅读:
    向net core 3.0进击——多平台项目发布与部署
    Linux配置部署_新手向(五)——Docker的安装与使用
    Ubuntu 18.04使用OpenSSL自签证书(证书支持多IP及多域名,谷歌浏览器无警告)
    学习makefile的一个工程示例
    Centos7下设置ceph 12.2.1 (luminous)dashboard UI监控功能
    一个小例子学习makefile
    Centos7下部署ceph 12.2.1 (luminous)集群及RBD使用
    VMware Centos7 桥接 DHCP无法获得IP
    VMware Centos7 NAT 无法上网的解决方法
    jerasure 2.0译文
  • 原文地址:https://www.cnblogs.com/huanzi-qch/p/10281773.html
Copyright © 2020-2023  润新知