Panda ORM源码-数据库表自动生成Java实体类

2019-06-15 00:00:00 orm 源码 Panda

一,整体思路

1,首先明确目标,需要通过指定数据库名,自动生成该数据库中所有表对应的Java实体类。

2,数据库名及其他一些参数(跟数据库url、用户、密码、实体类保存路径)通过配置文件设定。

3,连接数据库后,通过数据库名获取该数据库中所有的表。对于MySQL来说,可以通过select table_name from information_schema.tables where table_schema='XXX',来获取XXX数据库中所有的表名。PS:对于information_schema数据库不了解的可以打开看下,information_schema.tables保存了运行在MySQL上表的信息。

4,通过Java代码读取表结构信息,生成对应的Java类字符串,将字符串写入文件。

二,参数设置

参数保存在src目录下config.properties文件下,通过Constants的static方法获取配置文件信息,具体代码如下:

config.properties代码如下,注意前五个参数为数据库连接需要的参数,table_schema表示数据库名,entity_package_name表示生成实体类的包名,两个path分别表示实体类文件及日志保存路径。

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/bank_performance?useUnicode=true&characterEncoding=utf-8
user=root
password=Pass1234
table_schema=bank_performance
entity_package_name=panda.bank.entity
file_save_path=D\:\\Java\\AutoEntity\\
file_log_path=D\:\\Java\\AutoEntity\\

读取配置文件是通过Constants 中的static方法实现:

package panda.orm.util;
public class Constants {
        //Property类代码在下面
    public static Property CONFIG_PROPERTY;
    public static String getFormatDateTime(){
        SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return formater.format(new Date());
    }
    public static String getLogFormatDateTime(){
        SimpleDateFormat formater = new SimpleDateFormat("yyyyMMddhhmmss");
        return formater.format(new Date());
    }
    static{
        Properties fileProperties = new Properties();  
        InputStream inputStream = fileProperties.getClass().getResourceAsStream("/config.properties");  
        CONFIG_PROPERTY=new Property();
        try{  
            fileProperties.load(inputStream);  
            CONFIG_PROPERTY.setDriver(fileProperties.getProperty("driver"));  
            CONFIG_PROPERTY.setUrl(fileProperties.getProperty("url"));  
            CONFIG_PROPERTY.setUser(fileProperties.getProperty("user"));  
            CONFIG_PROPERTY.setPassword(fileProperties.getProperty("password"));  
            CONFIG_PROPERTY.setTable_schema(fileProperties.getProperty("table_schema"));  
            CONFIG_PROPERTY.setFile_save_path(fileProperties.getProperty("file_save_path"));  
            CONFIG_PROPERTY.setFile_log_path(fileProperties.getProperty("file_log_path"));  
            CONFIG_PROPERTY.setEntity_package_name(fileProperties.getProperty("entity_package_name"));  
        }catch (IOException ex){  
            ex.printStackTrace();  
        }finally{  
            try {
                inputStream.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }  
        }  
    }
}

package panda.orm.util;
public class Property{
    private String driver;
    private String url;
    private String user;
    private String password;
    private String table_schema;
    private String file_save_path;  
    private String file_log_path;
    private String entity_package_name;
        //省略get set

三,EntityGenerater.generateClasses()方法直接生成数据库中所有表对应实体类,直接上代码看注释部分即可:

package panda.orm.util;
import java.io.File;
import java.io.FileOutputStream;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import panda.orm.database.MySQLHandler;
import panda.orm.exception.SqlExcuteException;
/**
 * 由MySQL数据库自动生成Java实体类
 * @author 猫哥
 * @date 2017.3.23
 */
public class EntityGenerater {
    //在配置完config.properties之后,直接调用EntityGenerater类的静态方法generateClasses即可生成对应Java实体类
    public static void main(String[] args) {
        EntityGenerater.generateClasses();
    }
    //generateClasses方法做了三件事
    //1,构造方法获取需要的参数
    //2,init方法生成数据库对应的表-类结构
    //3,write方法负责将类结构转换为实体类代码字符串并写入文件
    public static void generateClasses(){
        EntityGenerater eg=new EntityGenerater(Constants.CONFIG_PROPERTY.getTable_schema(),
                Constants.CONFIG_PROPERTY.getEntity_package_name(),Constants.CONFIG_PROPERTY.getFile_save_path());
        eg.init();
        eg.write();
        System.out.println("Panda ORM generate success!");
    }
    //tables保存了数据库的结构,tables的key表示表名,key对应的结合表示该表的所有列名集合
    private Map<String,Set<String>> tables=new HashMap<String,Set<String>>();
    private String table_schema;
    private String entity_package_name;
    private String entity_save_path;
    public EntityGenerater(String table_schema,String entity_package_name,String entity_save_path){
        this.table_schema=table_schema;
        this.entity_package_name=entity_package_name;
        this.entity_save_path=entity_save_path;
    }
    public void init(){
        MySQLHandler hand=new MySQLHandler();
        ResultSet rs=null;
        //数据库名
        String table_schema =Constants.CONFIG_PROPERTY.getTable_schema();
        //获取数据库下所有表的sql语句
        String sql="select table_name from information_schema.tables where table_schema='"+table_schema+"'";
        tables.clear();
        try {
            rs=hand.query(sql);
            while(rs.next()){
                String tableName=rs.getString("table_name");
                //getColumns方法获取表对应的列
                Set<String> columns=getColumns(tableName);
                //将表和对应列结合放入tables
                tables.put(tableName, columns);
            }
        } catch (Exception ex) {
            new SqlExcuteException(ex.getMessage(),this.getClass().getName(),"sql执行异常",sql);
        }finally{
            hand.sayGoodbye();
        }
    }
    //获取表对应的列名集合
    private Set<String> getColumns(String tableName){
        MySQLHandler hand=new MySQLHandler();
        ResultSet rs=null;
        String sql="select * from "+tableName;
        Set<String> columns=new HashSet<String>();
        try {
            rs=hand.query(sql);
            ResultSetMetaData meta=rs.getMetaData();
            int count=meta.getColumnCount();
            for(int i=0;i<meta.getColumnCount();i++){
                columns.add(meta.getColumnName(i+1));
            }
        }catch (Exception ex) {
            new SqlExcuteException(ex.getMessage(),this.getClass().getName(),"sql执行异常",sql);
        }finally{
            hand.sayGoodbye();
        }
        return columns;
    }
    //转换tables信息为实体类代码对应的字符串,并写入文件
    public void write(){
        Iterator iter = tables.entrySet().iterator();
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            String tableName = (String)entry.getKey();
            Set<String> columns = (Set<String>)entry.getValue();
            //核心方法,将表明和对应列信息转换为实体类代码
            String entityString=getEntityString(tableName,columns);
            //写文件
            try{
                String path=entity_save_path+turnFirstUp(tableName)+".java";
                File file=new File(path);
                if(!file.exists())
                    file.createNewFile();
                FileOutputStream out=new FileOutputStream(file,true);       
                out.write(entityString.getBytes("utf-8"));
                out.close();
            }
            catch(Exception ex){
                ex.printStackTrace();
            }          
        }
    }
    //核心方法,将表明和对应列信息转换为实体类代码
    private String getEntityString(String tableName,Set<String> columns){
        StringBuilder sb=new StringBuilder();
        //包名及类注释
        sb.append("package "+entity_package_name+";\r\n");
        sb.append("/**\r\n");
        sb.append("* " + new Date() + "\r\n");
        sb.append("* Panda ORM atuo generate: " + tableName + " \r\n");
        sb.append("*/ \r\n");   
        //类定义
        sb.append("public class " + turnFirstUp(tableName) + "{\r\n");
        //属性
        for(String colName:columns){
            sb.append("\tprivate String "+colName+";\r\n");
        }
        //方法
        for(String colName:columns){
            sb.append("\tpublic String get"+turnFirstUp(colName)
                    +"(){\r\n");
            sb.append("\t\treturn "+colName+";\r\n");
            sb.append("\t}\r\n");
            sb.append("\tpublic void set"+turnFirstUp(colName)
                    + "(String "+colName+"){\r\n");
            sb.append("\t\tthis."+colName+"="+colName+";\r\n");
            sb.append("\t}\r\n");
        }
        sb.append("}");
        return sb.toString();
    }
    //首字母大写
    private String turnFirstUp(String str) {
        char[] ch = str.toCharArray();
        if(ch[0]>='a'&&ch[0]<='z'){
            ch[0]=(char)(ch[0]-32);
        }
        return new String(ch);
    }
}

四,测试,以马上要开始讲解的银行业绩系统数据库为例,表结构如下图(具体含义先不解释,只是演示由数据库生成实体代码):

《Panda ORM源码-数据库表自动生成Java实体类》

然后运行,注意配置文件就按开头说的配置的。

public static void main(String[] args) {
        EntityGenerater.generateClasses();
    }

运行后,控制台提示:Panda ORM generate success!,表示成功,根据配置文件在目录下发现:
《Panda ORM源码-数据库表自动生成Java实体类》

打开User.java,成功。

package panda.bank.entity;
/**
* Thu Mar 23 11:30:12 CST 2017
* Panda ORM atuo generate: user 
*/ 
public class User{
    private String user_name;
    private String user_role;
    private String user_job;
    private String user_password;
    private String user_id;
    private String user_department;
    public String getUser_name(){
        return user_name;
    }
    public void setUser_name(String user_name){
        this.user_name=user_name;
    }
    public String getUser_role(){
        return user_role;
    }
    public void setUser_role(String user_role){
        this.user_role=user_role;
    }
    public String getUser_job(){
        return user_job;
    }
    public void setUser_job(String user_job){
        this.user_job=user_job;
    }
    public String getUser_password(){
        return user_password;
    }
    public void setUser_password(String user_password){
        this.user_password=user_password;
    }
    public String getUser_id(){
        return user_id;
    }
    public void setUser_id(String user_id){
        this.user_id=user_id;
    }
    public String getUser_department(){
        return user_department;
    }
    public void setUser_department(String user_department){
        this.user_department=user_department;
    }
}

五,补充说明

根据Panda ORM整体的设计,所有类属性都是String类型,其实可以根据表中的数据类型转换属性类型,此处不再加以演示。

有了实体类代码后,需要添加主键、外键注释,并需要将外键对应属性类型改为外键指向表实体类型,然后就可以直接利用Panda ORM生成常用的增、删、改、查方法。

增、删、改没有问题,查的话只提供了最基本的查数量、分页查、查全部、按主键查的功能。更加复杂的查询还是要写原生的sql代码,有点遗憾。但是想想MyBatis每个都要单独写sql和Hibernate复杂的配置,心里又好受了很多,哈哈。下篇演示通过注解实现ORM的思路和源码。

相关文章