From c91477550f48914647dfb4c05f62a6c8327f1ac4 Mon Sep 17 00:00:00 2001 From: llopez Date: Mon, 25 Mar 2024 08:40:43 -0500 Subject: [PATCH] first commit --- nb-configuration.xml | 18 ++ pom.xml | 26 ++ .../entidades/GeneradorEntidades.java | 271 ++++++++++++++++++ .../entidades/coneccion/Conexion.java | 139 +++++++++ .../constantes/GenedorConstantes.java | 72 +++++ .../entidades/GeneradorEntidades.class | Bin 0 -> 10872 bytes .../entidades/coneccion/Conexion.class | Bin 0 -> 3611 bytes .../constantes/GenedorConstantes.class | Bin 0 -> 2473 bytes .../compile/default-compile/createdFiles.lst | 0 .../compile/default-compile/inputFiles.lst | 3 + 10 files changed, 529 insertions(+) create mode 100644 nb-configuration.xml create mode 100644 pom.xml create mode 100644 src/main/java/com/qsoft/generador/entidades/GeneradorEntidades.java create mode 100644 src/main/java/com/qsoft/generador/entidades/coneccion/Conexion.java create mode 100644 src/main/java/com/qsoft/generador/entidades/constantes/GenedorConstantes.java create mode 100644 target/classes/com/qsoft/generador/entidades/GeneradorEntidades.class create mode 100644 target/classes/com/qsoft/generador/entidades/coneccion/Conexion.class create mode 100644 target/classes/com/qsoft/generador/entidades/constantes/GenedorConstantes.class create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/nb-configuration.xml b/nb-configuration.xml new file mode 100644 index 0000000..778cb62 --- /dev/null +++ b/nb-configuration.xml @@ -0,0 +1,18 @@ + + + + + + JDK_21 + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c8d814c --- /dev/null +++ b/pom.xml @@ -0,0 +1,26 @@ + + + 4.0.0 + com.qsoft.generador.entidades + generador-entidades + 1.0-SNAPSHOT + jar + + UTF-8 + 17 + 17 + com.qsoft.generador.entidades.GeneradorEntidades + + + + com.mysql + mysql-connector-j + 8.2.0 + + + org.apache.commons + commons-lang3 + 3.14.0 + + + \ No newline at end of file diff --git a/src/main/java/com/qsoft/generador/entidades/GeneradorEntidades.java b/src/main/java/com/qsoft/generador/entidades/GeneradorEntidades.java new file mode 100644 index 0000000..97c3f5c --- /dev/null +++ b/src/main/java/com/qsoft/generador/entidades/GeneradorEntidades.java @@ -0,0 +1,271 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Project/Maven2/JavaApp/src/main/java/${packagePath}/${mainClassName}.java to edit this template + */ + +package com.qsoft.generador.entidades; + +import org.apache.commons.lang3.StringUtils; +import com.qsoft.generador.entidades.coneccion.Conexion; +import com.qsoft.generador.entidades.constantes.GenedorConstantes; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author developer2 + */ +public class GeneradorEntidades { + + public static void main(String[] args) { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //Ya tenemos el "lector" + String mostrarTablas = "SHOW FULL TABLES FROM %s"; + String driver = "com.mysql.cj.jdbc.Driver"; + String database = "prueba"; + String host = "localhost"; + String port = "3306"; + String url = ""; + String user = "christian"; + String pass = "12345678"; + String linea = null; + String claveCompuesta = null; + try { + System.out.println(String.format(">>>>>>>>>>>> PENSADO EN SPRING<<<<<<<<<", driver)); + System.out.println(String.format("Driver [%s]:", driver)); + linea =br.readLine(); + driver = linea == null || linea.trim().equals("") ? driver : linea ; + + System.out.println(String.format("Database [%s]:", database)); + linea = br.readLine(); + database = linea == null || linea.trim().equals("") ? database : linea ; + + System.out.println(String.format("Host [%s]:", host)); + linea = br.readLine(); + host = linea == null || linea.trim().equals("") ? host : linea ; + + System.out.println(String.format("Port [%s]:", port)); + linea = br.readLine(); + port = linea == null || linea.trim().equals("") ? port : linea; + + url = String.format("jdbc:mysql://%s:%s/%s?useSSL=false", host,port,database); + System.out.println("URL: "+url); + + System.out.println(String.format("user [%s]:", user)); + linea = br.readLine(); + user = linea == null || linea.trim().equals("") ? user : linea; + + System.out.println(String.format("pass [%s]:", pass)); + linea = br.readLine(); + pass = linea == null || linea.trim().equals("") ? pass : linea; + + Conexion con = new Conexion(driver, database, host, port, url, user, pass); + + Connection conection = con.crearConexionMysql(); + System.out.println("Conexion>>"+conection); + + ResultSet rs = con.crearResult(conection,String.format(mostrarTablas, database)); + String nombreTabla; + ResultSet tb; + ResultSet tbnoPk; + String clase; + String tipoDato; + String paquete = "com.qsoft.ejemplo.rs.model"; + + String columna = null; + System.out.println(String.format("Paquete [%s]:", paquete)); + linea = br.readLine(); + paquete = linea == null || linea.trim().equals("") ? paquete : linea; + boolean auto_inc= Boolean.FALSE; + while (rs.next()) { /** recorre tablas*/ + StringBuilder paquetes = new StringBuilder("package "); + nombreTabla = rs.getString(1); + StringBuilder codigoClase = new StringBuilder(String.format("@Entity\n@Table(name = \"%s\")\n", nombreTabla)); + StringBuilder gettersSetters = new StringBuilder(String.format("@Entity\n@Table(name = \"%s\")\n", nombreTabla)); + codigoClase.append("public class ").append(StringUtils.capitalize(toCamelCase(nombreTabla))).append(" implements Serializable {\n\n"); + codigoClase.append("private static final long serialVersionUID = 1L;\n\n"); + tb = con.crearResult(conection, String.format(GenedorConstantes.SQL_CAMPOS_RELACIONES.concat(" and tc.CONSTRAINT_TYPE = 'PRIMARY KEY';"), database, nombreTabla)); + System.out.println(String.format(">>>>>>>>>>>>>>>>>>>>>>%s<<<<<<<<<<<<<<<<<<", nombreTabla)); + + paquetes.append(paquete).append(";\n"); + paquetes.append("import jakarta.persistence.*;\n"); + // primary key + int numeroFilas = 0; + List pks = new ArrayList<>(); + + while (tb.next()) { + try { + Integer.parseInt(tb.getObject(15).toString()); + auto_inc = Boolean.TRUE; + } catch (Exception e) { + auto_inc = Boolean.FALSE; + } + + numeroFilas++; + columna = tb.getString(3); + pks.add(String.format(GenedorConstantes.TAB.concat("private %s %s;\n\n"), tipoValor(tb.getString(5), tb.getInt(7), tb.getInt(8)),toCamelCase(columna))); + } + if(numeroFilas == 1){ + codigoClase.append(String.format(auto_inc? GenedorConstantes.FORMATO_ANOTACIONES_AUTO_INCREMENT_PK : GenedorConstantes.FORMATO_ANOTACIONES_PK, columna)); + codigoClase.append(pks.get(0)); + + } + if(numeroFilas> 1){ + claveCompuesta = procesarClavePrimaria(pks, paquete, nombreTabla,auto_inc); + codigoClase.append("\n@EmbeddedId\n"); + codigoClase.append(String.format("private %s %s;\n\n",StringUtils.capitalize(toCamelCase(nombreTabla)).concat("PK"), toCamelCase(nombreTabla).concat("PK"))); + } + + tbnoPk = con.crearResult(conection, String.format(GenedorConstantes.SQL_CAMPOS_RELACIONES.concat(" and tc.CONSTRAINT_TYPE <> 'PRIMARY KEY';"), database, nombreTabla)); + while (tbnoPk.next()) { + if(tb.getString(11).equals("FOREIGN KEY")){ + columna = tb.getString(3); + tipoDato = StringUtils.capitalize(tb.getString(13)); + } + numeroFilas++; + columna = tb.getString(3); + pks.add(String.format(GenedorConstantes.TAB.concat("private %s %s;\n\n"), tipoValor(tb.getString(5), tb.getInt(7), tb.getInt(8)),columna)); + + } + System.out.println("CLAVE>>\n"+codigoClase.toString()); + System.out.println("CLAVE>>\n"+claveCompuesta); + + } + + } catch (IOException|SQLException ex) { + ex.printStackTrace(); + } + } + /** + * el + */ + public static String toCamelCase(String texto){ + StringBuilder sb = new StringBuilder(); + String []textoSeparado = null; + if(texto.contains(" ")){ + textoSeparado = texto.trim().split(" "); + } + if(texto.contains("-")){ + textoSeparado = texto.trim().split("-"); + } + if(texto.contains("_")){ + textoSeparado = texto.trim().split("_"); + } + if(textoSeparado == null){ + textoSeparado = new String[]{texto}; + } + + + for (int i = 0; i < textoSeparado.length; i++) { + if(i == 0){ + sb.append(textoSeparado[i].toLowerCase()); + }else{ + sb.append(StringUtils.capitalize(texto.toLowerCase())); + } + } + return sb.toString(); + } + + public static String procesarClavePrimaria(List pks, String paquete, String nombreTabla,boolean auto_inc) { + StringBuilder sb ; + StringBuilder packetes ; + StringBuilder constructores; + StringBuilder gettersSetters; + String[] variableSplit; + String nombreDato = null; + StringBuilder valorPk; + + sb = new StringBuilder(GenedorConstantes.COMENTARIO_DESARROLLADOR); + packetes = new StringBuilder("package ").append(paquete).append(";\n"); + packetes.append("import jakarta.persistence.*;\nimport java.io.Serializable;\n"); + constructores = new StringBuilder(String.format(GenedorConstantes.FORMATO_CONSTRUCTOR, StringUtils.capitalize(nombreTabla).concat("PK"),"")); + constructores.append(GenedorConstantes.TAB).append("public ").append(StringUtils.capitalize(nombreTabla)).append("PK("); + + gettersSetters = new StringBuilder(); + sb.append("@Embeddable\npublic class "); + sb.append(StringUtils.capitalize(nombreTabla)).append("PK"); + sb.append(" implements Serializable {\n"); + int i = 0; + for (String pk : pks) { + variableSplit = pk.trim().split(" "); + + nombreDato = variableSplit[2].split(";")[0]; + + gettersSetters.append(String.format(GenedorConstantes.FORMATO_GET, variableSplit[1],StringUtils.capitalize(nombreDato),nombreDato)); + gettersSetters.append(String.format(GenedorConstantes.FORMATO_SET, StringUtils.capitalize(nombreDato),variableSplit[1],nombreDato,nombreDato,nombreDato)); + if(pk.contains("Date")){ + packetes.append("import java.util.Date;\n"); + } + if(pk.contains("BigDecimal")){ + packetes.append("import java.math.BigDecimal;\n"); + } + if(pk.contains("BigInteger")){ + packetes.append("import java.math.BigInteger;\n"); + } + constructores.append(variableSplit[1]).append(" ").append(nombreDato); + if(i != (pks.size()-1)){ + constructores.append(", "); + } + sb.append(GenedorConstantes.TAB).append("@Basic(optional = false)\n"); + if(i == 0 && auto_inc){ + sb.append(GenedorConstantes.TAB).append("@GeneratedValue(strategy = GenerationType.IDENTITY)\n"); + } + sb.append(GenedorConstantes.TAB).append(String.format("@Column(name = \"%s\")\n", nombreDato)); + sb.append(pk); + i++; + } + constructores.append("){\n }\n\n"); + valorPk = new StringBuilder(packetes).append(sb.append(constructores.append(gettersSetters.append("\n}")))); + + return valorPk.toString(); + } + + public static String tipoValor(String tipo, int presicion, int escala){ + String tipoJava = "String"; + + switch (tipo.toLowerCase()) { + case "int": + tipoJava = "Integer"; + if(escala == 0 ){ + if(presicion > 5){ + tipoJava = "Long"; + } + if(presicion == 1){ + tipoJava = "Short"; + } + + }else{ + tipoJava = "Double"; + + if(presicion > 7 && escala>2){ + tipoJava = "BigDecimal"; + } + } + break; + case "varchar": + tipoJava = "String"; + break; + case "number": + tipoJava = "Integer"; + break; + case "date": + tipoJava = "Date"; + break; + case "datetime": + tipoJava = "Date"; + break; + default: + throw new AssertionError(); + } + return tipoJava; + + + } +} diff --git a/src/main/java/com/qsoft/generador/entidades/coneccion/Conexion.java b/src/main/java/com/qsoft/generador/entidades/coneccion/Conexion.java new file mode 100644 index 0000000..42e765a --- /dev/null +++ b/src/main/java/com/qsoft/generador/entidades/coneccion/Conexion.java @@ -0,0 +1,139 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package com.qsoft.generador.entidades.coneccion; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * + * @author developer2 + */ +public class Conexion { + private String driver; + private String database; + private String host; + private String port; + private String url; + private String user; + private String pass; + + /** + Crear conexion a mysql + */ + public Connection crearConexionMysql(){ + Connection con = null; + try { + Class.forName(driver); + con = DriverManager.getConnection(url, user, pass); + } catch (ClassNotFoundException | SQLException e) { + e.printStackTrace(); + } + + return con; + } + public void cerrar(Connection con) throws SQLException { + if (con != null) { + con.close(); + } + } + + + + public ResultSet crearResult(Connection con, String consulta) throws SQLException{ + ResultSet rs = null; + PreparedStatement stmt = con.prepareStatement(consulta) ; + rs = stmt.executeQuery(); + + return rs; + } + + private void setBasics(){ + driver = "com.mysql.cj.jdbc.Driver"; + port = "3306"; + } + + public Conexion() { + setBasics(); + } + /** + * Constructor + */ + public Conexion(String driver, String database, String host, String port, String url, String user, String pass) { + this.driver = driver; + this.database = database; + this.host = host; + this.port = port; + this.url = url; + this.user = user; + this.pass = pass; + } + /** + * GETERS Y SETTERS + */ + public String getDriver() { + return driver; + } + + public void setDriver(String driver) { + this.driver = driver; + } + + public String getDatabase() { + return database; + } + + public void setDatabase(String database) { + this.database = database; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public String getPass() { + return pass; + } + + public void setPass(String pass) { + this.pass = pass; + } + + + + + +} diff --git a/src/main/java/com/qsoft/generador/entidades/constantes/GenedorConstantes.java b/src/main/java/com/qsoft/generador/entidades/constantes/GenedorConstantes.java new file mode 100644 index 0000000..262d23c --- /dev/null +++ b/src/main/java/com/qsoft/generador/entidades/constantes/GenedorConstantes.java @@ -0,0 +1,72 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package com.qsoft.generador.entidades.constantes; + +/** + * + * @author developer2 + */ +public class GenedorConstantes { + public static final String TAB = " "; + public static final String COMENTARIO_DESARROLLADOR = "/**\n *\n * @author Christian Ruales\n */\n"; + public static final String ESQUEMA_CLASE = "package %s;\n" + +"import jakarta.persistence.*;\n" + +"\n" + +"@Entity\n" + +"@Table(name = \"%s\")\n" + +"public class %s {\n" + +"\n" + +" public %s() {\n" + +" }\n" + +"\n" + +"\n" + +" public %s(%s %s) {\n" + +" this.%s = %s;\n" + +" }\n" + +"\n" + +"%s" + +"\n" + +"}"; + public static final String FORMATO_PK = " @Id\n" + +" @GeneratedValue(strategy = GenerationType.IDENTITY)\n" + +" @Column(name = \"%s\")\n" + +" private %s %s;\n" +";"; + public static final String FORMATO_ANOTACIONES_AUTO_INCREMENT_PK = " @Id\n" + +" @GeneratedValue(strategy = GenerationType.IDENTITY)\n" + +" @Column(name = \"%s\")\n" ; + public static final String FORMATO_ANOTACIONES_PK = " @Id\n" + +" @Basic(optional = false)\n" + +" @Column(name = \"%s\")\n"; + public static final String FORMATO_CAMPO_NORMAL= " @Column(name = \"%s\")\n" + +" private %s %s;\n" ; + public static final String FORMATO_CAMPO_RELACION = " @JoinColumn(name = \"%s\", insertable = false, updatable = false)\n" + +" @ManyToOne\n" + +" private %s %s;\n"; + + public static final String FORMATO_CONSTRUCTOR = " public %s(%s) {\n" + +" }\n\n"; + + public static final String FORMATO_GET=" public %s get%s() {\n" + +" return %s;\n" + +" }\n" + +"\n"; + public static final String FORMATO_SET=" public void set%s(%s %s) {\n" + +" this.%s = %s;\n" + +" }\n\n"; + + public static final String SQL_CAMPOS_RELACIONES = "SELECT c.TABLE_SCHEMA,c.TABLE_NAME,c.COLUMN_NAME,c.ORDINAL_POSITION,c.DATA_TYPE,\n" + +"c.CHARACTER_MAXIMUM_LENGTH,c.NUMERIC_PRECISION,c.NUMERIC_SCALE,c.COLUMN_KEY,\n" + +"t.CONSTRAINT_NAME, tc.CONSTRAINT_TYPE,t.COLUMN_NAME, t.REFERENCED_TABLE_NAME, t.REFERENCED_COLUMN_NAME,tab.`AUTO_INCREMENT` \n" + +"FROM information_schema.`COLUMNS` c\n" + +" LEFT join information_schema.KEY_COLUMN_USAGE t on\n" + +" t.TABLE_SCHEMA = c.TABLE_SCHEMA and c.TABLE_NAME = t.TABLE_NAME \n" + +" AND c.COLUMN_NAME = t.COLUMN_NAME \n" + +" left join information_schema.TABLE_CONSTRAINTS tc on\n" + +" t.TABLE_SCHEMA = tc.TABLE_SCHEMA and t.TABLE_NAME = tc.TABLE_NAME \n" + +" and t.CONSTRAINT_NAME = tc.CONSTRAINT_NAME \n" + +"left join information_schema.TABLES tab on\n" + +" t.TABLE_SCHEMA = tab.TABLE_SCHEMA and t.TABLE_NAME = tab.TABLE_NAME \n"+ +"where c.TABLE_SCHEMA = '%s' and c.TABLE_NAME = '%s'" ; +} diff --git a/target/classes/com/qsoft/generador/entidades/GeneradorEntidades.class b/target/classes/com/qsoft/generador/entidades/GeneradorEntidades.class new file mode 100644 index 0000000000000000000000000000000000000000..e885a7daf90f87d274e212ca39442b0a872cad5e GIT binary patch literal 10872 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