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;\n" +
- "\n" +
- "import lombok.Data;\n" +
- "import javax.persistence.*;\n" +
- "import java.io.Serializable;\n" +
- "import java.util.Date;\n" +
- "\n" +
- "@Entity\n" +
- "@Table(name = \"" + tableName + "\")\n" +
- "@Data\n" +
- "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + " implements Serializable {\n"
- );
- //遍历设置属性
- for (TableInfo tableInfo : tableInfos) {
- //主键
- if ("PRI".equals(tableInfo.getColumnKey())) {
- stringBuffer.append(" @Id\n");
- }
- //自增
- if ("auto_increment".equals(tableInfo.getExtra())) {
- stringBuffer.append(" @GeneratedValue(strategy= GenerationType.IDENTITY)\n");
- }
- stringBuffer.append(" private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "\n\n");
- }
- 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;\n" +
- "\n" +
"import "+ basePackage_.replaceAll("\\\\", ".") +" common.pojo.PageCondition;"+- "import lombok.Data;\n" +
- "import java.io.Serializable;\n" +
- "import java.util.Date;\n" +
- "\n" +
- "@Data\n" +
- "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo extends PageCondition implements Serializable {\n"
- );
- //遍历设置属性
- for (TableInfo tableInfo : tableInfos) {
- stringBuffer.append(" private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "\n\n");
- }
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.repository.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import org.springframework.stereotype.Repository;\n" +
- "\n" +
- "@Repository\n" +
- "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository extends CommonRepository<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
- );
- stringBuffer.append("\n");
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.service.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" +
- "\n" +
- "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service extends CommonService<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
- );
- stringBuffer.append("\n");
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.service.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "repository." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository;\n" +
- "import org.springframework.beans.factory.annotation.Autowired;\n" +
- "import org.springframework.stereotype.Service;\n" +
- "import org.springframework.transaction.annotation.Transactional;\n" +
- "import javax.persistence.EntityManager;\n" +
- "import javax.persistence.PersistenceContext;\n" +
- "\n" +
- "@Service\n" +
- "@Transactional\n" +
- "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("\n\n");
- stringBuffer1.append(
- " @PersistenceContext\n" +
- " private EntityManager em;\n");
- stringBuffer1.append("" +
- " @Autowired\n" +
- " private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository " + StringUtil.camelCaseName(tableName) + "Repository;\n");
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.controller.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "service." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service;\n" +
- "import org.springframework.beans.factory.annotation.Autowired;\n" +
- "import org.springframework.web.bind.annotation.*;\n" +
- "\n" +
- "@RestController\n" +
- "@RequestMapping(\"/" + StringUtil.camelCaseName(tableName) + "/\")\n" +
- "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller extends CommonController<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
- );
- stringBuffer.append("\n");
- stringBuffer.append("" +
- " @Autowired\n" +
- " private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service " + StringUtil.camelCaseName(tableName) + "Service;\n");
- 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;\n" +
- "\n" +
- "import lombok.Data;\n" +
- "import javax.persistence.*;\n" +
- "import java.io.Serializable;\n" +
- "import java.util.Date;\n" +
- "\n" +
- "@Entity\n" +
- "@Table(name = \"" + tableName + "\")\n" +
- "@Data\n" +
- "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + " implements Serializable {\n"
- );
- //遍历设置属性
- for (TableInfo tableInfo : tableInfos) {
- //主键
- if ("PRI".equals(tableInfo.getColumnKey())) {
- stringBuffer.append(" @Id\n");
- }
- //自增
- if ("auto_increment".equals(tableInfo.getExtra())) {
- stringBuffer.append(" @GeneratedValue(strategy= GenerationType.IDENTITY)\n");
- }
- stringBuffer.append(" private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "\n\n");
- }
- 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;\n" +
- "\n" +
- "import "+ basePackage_.replaceAll("\\\\", ".") +" common.pojo.PageCondition;"+
- "import lombok.Data;\n" +
- "import java.io.Serializable;\n" +
- "import java.util.Date;\n" +
- "\n" +
- "@Data\n" +
- "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo extends PageCondition implements Serializable {\n"
- );
- //遍历设置属性
- for (TableInfo tableInfo : tableInfos) {
- stringBuffer.append(" private " + StringUtil.typeMapping(tableInfo.getDataType()) + " " + StringUtil.camelCaseName(tableInfo.getColumnName()) + ";//" + tableInfo.getColumnComment() + "\n\n");
- }
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.repository.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import org.springframework.stereotype.Repository;\n" +
- "\n" +
- "@Repository\n" +
- "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository extends CommonRepository<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
- );
- stringBuffer.append("\n");
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.service.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" +
- "\n" +
- "public interface " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service extends CommonService<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
- );
- stringBuffer.append("\n");
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.service.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "repository." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository;\n" +
- "import org.springframework.beans.factory.annotation.Autowired;\n" +
- "import org.springframework.stereotype.Service;\n" +
- "import org.springframework.transaction.annotation.Transactional;\n" +
- "import javax.persistence.EntityManager;\n" +
- "import javax.persistence.PersistenceContext;\n" +
- "\n" +
- "@Service\n" +
- "@Transactional\n" +
- "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("\n\n");
- stringBuffer1.append(
- " @PersistenceContext\n" +
- " private EntityManager em;\n");
- stringBuffer1.append("" +
- " @Autowired\n" +
- " private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Repository " + StringUtil.camelCaseName(tableName) + "Repository;\n");
- 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;\n" +
- "\n" +
- "import " + basePackage_.replaceAll("\\\\", ".") + "common.controller.*;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "pojo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ";\n" +
- "import " + package_.replaceAll("\\\\", ".") + "vo." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo;\n" +
- "import " + package_.replaceAll("\\\\", ".") + "service." + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service;\n" +
- "import org.springframework.beans.factory.annotation.Autowired;\n" +
- "import org.springframework.web.bind.annotation.*;\n" +
- "\n" +
- "@RestController\n" +
- "@RequestMapping(\"/" + StringUtil.camelCaseName(tableName) + "/\")\n" +
- "public class " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Controller extends CommonController<" + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Vo, " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + ", " + t + "> {"
- );
- stringBuffer.append("\n");
- stringBuffer.append("" +
- " @Autowired\n" +
- " private " + StringUtil.captureName(StringUtil.camelCaseName(tableName)) + "Service " + StringUtil.camelCaseName(tableName) + "Service;\n");
- 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
SpringBoot系列——Spring-Data-JPA(究极进化版) 自动生成单表基础增、删、改、查接口的更多相关文章
- spring-boot (三) spring data jpa
学习文章来自:http://www.ityouknow.com/spring-boot.html spring data jpa介绍 首先了解JPA是什么? JPA(Java Persistence ...
- springboot整合spring Data JPA
今天敲代码,一连串的错误,我也是服气~果然,我们不是在出bug,就是在找bug的路上…… 今天完成的是springboot整合spring data JPA ,出了一连串的错,真是头大 java.sq ...
- springboot整合spring data jpa 动态查询
Spring Data JPA虽然大大的简化了持久层的开发,但是在实际开发中,很多地方都需要高级动态查询,在实现动态查询时我们需要用到Criteria API,主要是以下三个: 1.Criteria ...
- springboot集成Spring Data JPA数据查询
1.JPA介绍 JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.它的出现主要是为 ...
- spring boot学习(4) SpringBoot 之Spring Data Jpa 支持(1)
第一节:Spring Data Jpa 简介 Spring-Data-Jpa JPA(Java Persistence API)定义了一系列对象持久化的标准,目前实现这一规范的产品有Hibernate ...
- IntelliJ IDEA 2017版 spring-boot使用Spring Data JPA搭建基础版的三层架构
1.配置环境pom <?xml version="1.0" encoding="UTF-8"?> <project xmlns="h ...
- IntelliJ IDEA 2017版 spring-boot使用Spring Data JPA使用Repository<T, T>编程
1.环境搭建pom.xml搭建 <?xml version="1.0" encoding="UTF-8"?> <project xmlns=& ...
- spring boot学习(5) SpringBoot 之Spring Data Jpa 支持(2)
第三节:自定义查询@Query 有时候复杂sql使用hql方式无法查询,这时候使用本地查询,使用原生sql的方式: 第四节:动态查询Specification 使用 什么时候用呢?比如搜索有很多条 ...
- PostgreSQL 、springboot 、spring data jpa 集成
项目地址:https://gitee.com/zhxs_code/PostgreSQL_springboot_jpa_demo.git 增删查改都已经实现. 重点部分: 1.定义自己的方言. pack ...
随机推荐
- 张高兴的 Windows 10 IoT 开发笔记:串口红外编解码模块 YS-IRTM
This is a Windows 10 IoT Core project on the Raspberry Pi 2/3, coded by C#. GitHub: https://github.c ...
- python设计模式-观察者
定义: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖的对象都会得到通知并被自动更新. 观察者模式是对象的行为模式,又叫发布-订阅(pubish/subscribe)模式,模型 ...
- 【Java】广州三本秋招经历
前言 只有光头才能变强 离上次发文章已经快两个月时间了,最近一直忙着秋招的事.今天是2018年10月22日,对于互联网行业来说,秋招就基本结束了.我这边的流程也走完了(不再笔试/面试了),所以来写写我 ...
- Spring Boot整合Mybatis并完成CRUD操作
MyBatis 是一款优秀的持久层框架,被各大互联网公司使用,本文使用Spring Boot整合Mybatis,并完成CRUD操作. 为什么要使用Mybatis?我们需要掌握Mybatis吗? 说的官 ...
- 解决Error:All flavors must now belong to a named flavor dimension. Learn more at...
低版本的gradle里面不会出现这个错误,高版本出现,不多说,看如何解决 在defaultConfig{}中添加:flavorDimensions "default" 保证所有的f ...
- 还在用AIDL吗?试试EasyMessenger吧
EasyMessenger 直达Github项目地址 一款用于Android平台的基于Binder的进程间通信库,采用annotationProcessor生成IPC通信需要的代码.EasyMesse ...
- Kindle Windows版本 中文字体修改工具
近来想要用Windows看Kindle电子书,无奈Windows 版本的Kindle不能修改中文字体,非常难看.把Kindle拉到IDA PRO看了一下,发现Kindle Windows版本的中文字体 ...
- Jmeter + Grafana搭建实时监控可视化
小贴士: 建议使用jmeter3.3+版本,在这个版本以后才有backend listenter 对接influxDB. Jmeter中backend listenter如图 influxdbUr ...
- SVN的安装与配置
单独安装 SVN:1.安装:yum -y install subversionmkdir -p /data/svn/ 创建创库目录svnadmin create /data/svn/repos 构建版 ...
- Mysql B+Tree原理
B+树索引是B+树在数据库中的一种实现,是最常见也是数据库中使用最为频繁的一种索引.B+树中的B代表平衡(balance),而不是二叉(binary),因为B+树是从最早的平衡二叉树演化而来的.在讲B ...