MyBatis Generator (MGB): Generador de código para MyBatis e iBATIS
Introducción
En cualquier proyecto suelen haber tareas que pueden generarse de manera automática ahorrándose grandes esfuerzos y evitando los errores que podrían ser introduccidos si se hubieran realizado manualmente.
Claro está que el resultado de la generación automática tiene ser calidad y que los retoques y adaptaciones necesarias para incluirlo en nuestros proyectos deben ser mímimas.
En este tutorial vamos a hacer una introducción de MyBatis Generator (MGB) un generador de código para el framework de presistencia MyBatis desarrollado por Apache (y con licencia Apache License).
MGB
se apoya en JDBC, para realizar una reflexión o introspección de una fuente de datos para generar automáticamente las clases del modelo, de acceso a datos y los archivos de mapeo (SQL Map XML).
MGB es una aplicación (es un JAR sin dependencias) bastante configurable a través de un archivo XML y puede ser invocado desde Ant, Maven o desde línea de comandos.
El producto y la documentación (bastante buena) puede descargarse desde página de descarga.
Un ejemplo
Para ver como funciona, partimos de una base de datos MySql ubicada en el esquema de nombre «cgarcia1».
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
CREATE TABLE companies ( id INT(3) UNSIGNED NOT NULL AUTO_INCREMENT, companykey VARCHAR(25) NOT NULL, name VARCHAR(100) NOT NULL, url VARCHAR(255), publicity TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, userlicenses INT(10) UNSIGNED NOT NULL DEFAULT 20, teacherlicenses INT(10) UNSIGNED NOT NULL DEFAULT 1, email VARCHAR(50) NOT NULL, state TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, messages TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, notes TINYINT(1) UNSIGNED NOT NULL DEFAULT 1, autoRegisterPwd VARCHAR(15), language VARCHAR(2) NOT NULL DEFAULT 'es', hddMbSpace INT(5) NOT NULL DEFAULT 5, PRIMARY KEY (id), UNIQUE companyKeyIDX (companykey) ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci; CREATE TABLE teachers ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, companyid INT(3) UNSIGNED NOT NULL, teacherkey VARCHAR(25) NOT NULL, pwd VARCHAR(50) NOT NULL, name VARCHAR(70) NOT NULL, email VARCHAR(50) NOT NULL, notes VARCHAR(255), testlicenses INT(3) UNSIGNED DEFAULT 100, PRIMARY KEY (id), UNIQUE teacherIDX (companyid, teacherkey), KEY companyIDX (companyid), FOREIGN KEY (companyid) REFERENCES companies(id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci; CREATE TABLE teacherNotesCategories ( id INT(3) UNSIGNED NOT NULL AUTO_INCREMENT, teacherid INT(10) UNSIGNED NOT NULL, description VARCHAR(128) DEFAULT NULL, PRIMARY KEY (id), KEY teacherNotesCategoriesIDX (teacherid), FOREIGN KEY (teacherid) REFERENCES teachers(id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci; CREATE TABLE teacherNotes ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, teacherid INT(10) UNSIGNED DEFAULT NULL, categoryid INT(3) UNSIGNED DEFAULT NULL, published TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', subject VARCHAR(1000) DEFAULT NULL, content MEDIUMTEXT DEFAULT NULL, PRIMARY KEY (id), KEY teacherNotesIDX (teacherid), KEY teacherCategoryIDX (categoryid), FOREIGN KEY (teacherid) REFERENCES teachers(id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (categoryid) REFERENCES teacherNotesCategories(id) ON DELETE SET NULL ON UPDATE SET NULL ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci; CREATE TABLE users ( id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, companyid INT(3) UNSIGNED NOT NULL, userkey VARCHAR(25) NOT NULL, pwd VARCHAR(32) NOT NULL, lastNames VARCHAR(30), name VARCHAR(20), email VARCHAR(50) NOT NULL, registerDate TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', lastConnection TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', state TINYINT UNSIGNED NOT NULL DEFAULT 0, downloads INT(3) UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (id), UNIQUE userIDX (companyid, userkey), KEY companyIDX (companyid), FOREIGN KEY (companyid) REFERENCES companies(id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci; CREATE TABLE userPictures ( userid INT(10) UNSIGNED NOT NULL, mimetype VARCHAR(32), picture MEDIUMBLOB, PRIMARY KEY (userid), FOREIGN KEY (userid) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB CHARSET=utf8 collate=utf8_general_ci; |
generatorConfig.xml
A continuación exponemos el archivo de configuración necesario para generar el código fuente de nuestra aplicación.
Existen una gran cantidad de parámetros opcionales que nos permitirián adaptar la generación a nuestras necesidades y que se encuentran bastante bien documentadas en el archivo zip de descarga.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <!-- Ubicación del JAR JDBC del gestor de Base de datos a usar, en este caso MySQL --> <classPathEntry location="mysql-connector-java-5.1.14.jar" /> <!-- Generaremos para MyBatis versión 3 --> <context id="DB1" targetRuntime="MyBatis3"> <!-- No generamos los comentarios --> <commentGenerator> <property name="suppressAllComments" value="true"/> </commentGenerator> <!-- Parámetros de conexión a la bd --> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/cgarcia1" userId="invitado" password="invitado"> </jdbcConnection> <javaModelGenerator targetPackage="es.carlosgarcia.dao.model" targetProject="salida"> <property name="enableSubPackages" value="true" /> </javaModelGenerator> <sqlMapGenerator targetPackage="es.carlosgarcia.dao.sqlmap" targetProject="salida"> <property name="enableSubPackages" value="true" /> </sqlMapGenerator> <!-- También podríamos indicar el tipo ANNOTATEDMAPPER --> <javaClientGenerator type="XMLMAPPER" targetPackage="es.carlosgarcia.dao" targetProject="salida"> <property name="enableSubPackages" value="true" /> </javaClientGenerator> <!-- Generamos el código fuente de todas las tablas evitando la generación varios métodos --> <table tableName="%" enableCountByExample="false" enableDeleteByExample="false" enableUpdateByExample="false" selectByPrimaryKeyQueryId="false" selectByExampleQueryId="false" enableSelectByExample="false" modelType="flat"> <property name="useActualColumnNames" value="true"/> </table> </context> </generatorConfiguration> |
Suponiendo que el archivo de configuración generatorConfig.xml
, el driver JDBC y el JAR del generador de código están en el mismo directorio,
ejecutamos el comando:
java -jar mybatis-generator-core-1.3.1.jar -configfile generatorConfig.xml -overwrite
Captura de pantalla del resultado de la generación de código:
es.carlosgarcia.dao.model.Companies
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package es.carlosgarcia.dao.model; public class Companies { private Integer id; private String companykey; private String name; private String url; private Boolean publicity; private Integer userlicenses; private Integer teacherlicenses; private String email; private Boolean state; private Boolean messages; private Boolean notes; private String autoRegisterPwd; private String language; private Integer hddMbSpace; // ..................... // Getters/Setters // ..................... } |
es.carlosgarcia.dao.CompaniesMapper.java
1 2 3 4 5 6 7 8 9 10 11 12 |
package es.carlosgarcia.dao; import es.carlosgarcia.dao.model.Companies; public interface CompaniesMapper { int deleteByPrimaryKey(Integer id); int insert(Companies record); int insertSelective(Companies record); Companies selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Companies record); int updateByPrimaryKey(Companies record); } |
es.carlosgarcia.dao.sqlmap.CompaniesMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="es.carlosgarcia.dao.CompaniesMapper" > <resultMap id="BaseResultMap" type="es.carlosgarcia.dao.model.Companies" > <id column="id" property="id" jdbcType="INTEGER" /> <result column="companykey" property="companykey" jdbcType="VARCHAR" /> <result column="name" property="name" jdbcType="VARCHAR" /> <result column="url" property="url" jdbcType="VARCHAR" /> <result column="publicity" property="publicity" jdbcType="BIT" /> <result column="userlicenses" property="userlicenses" jdbcType="INTEGER" /> <result column="teacherlicenses" property="teacherlicenses" jdbcType="INTEGER" /> <result column="email" property="email" jdbcType="VARCHAR" /> <result column="state" property="state" jdbcType="BIT" /> <result column="messages" property="messages" jdbcType="BIT" /> <result column="notes" property="notes" jdbcType="BIT" /> <result column="autoRegisterPwd" property="autoRegisterPwd" jdbcType="VARCHAR" /> <result column="language" property="language" jdbcType="VARCHAR" /> <result column="hddMbSpace" property="hddMbSpace" jdbcType="INTEGER" /> </resultMap> <sql id="Base_Column_List" > id, companykey, name, url, publicity, userlicenses, teacherlicenses, email, state, messages, notes, autoRegisterPwd, language, hddMbSpace </sql> <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" > select 'false' as QUERYID, <include refid="Base_Column_List" /> from companies where id = #{id,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" > delete from companies where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" parameterType="es.carlosgarcia.dao.model.Companies" > insert into companies (id, companykey, name, url, publicity, userlicenses, teacherlicenses, email, state, messages, notes, autoRegisterPwd, language, hddMbSpace) values (#{id,jdbcType=INTEGER}, #{companykey,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, #{url,jdbcType=VARCHAR}, #{publicity,jdbcType=BIT}, #{userlicenses,jdbcType=INTEGER}, #{teacherlicenses,jdbcType=INTEGER}, #{email,jdbcType=VARCHAR}, #{state,jdbcType=BIT}, #{messages,jdbcType=BIT}, #{notes,jdbcType=BIT}, #{autoRegisterPwd,jdbcType=VARCHAR}, #{language,jdbcType=VARCHAR}, #{hddMbSpace,jdbcType=INTEGER}) </insert> <insert id="insertSelective" parameterType="es.carlosgarcia.dao.model.Companies" > insert into companies <trim prefix="(" suffix=")" suffixOverrides="," > <if test="id != null" > id, </if> <if test="companykey != null" > companykey, </if> <if test="name != null" > name, </if> <if test="url != null" > url, </if> <if test="publicity != null" > publicity, </if> <if test="userlicenses != null" > userlicenses, </if> <if test="teacherlicenses != null" > teacherlicenses, </if> <if test="email != null" > email, </if> <if test="state != null" > state, </if> <if test="messages != null" > messages, </if> <if test="notes != null" > notes, </if> <if test="autoRegisterPwd != null" > autoRegisterPwd, </if> <if test="language != null" > language, </if> <if test="hddMbSpace != null" > hddMbSpace, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides="," > <if test="id != null" > #{id,jdbcType=INTEGER}, </if> <if test="companykey != null" > #{companykey,jdbcType=VARCHAR}, </if> <if test="name != null" > #{name,jdbcType=VARCHAR}, </if> <if test="url != null" > #{url,jdbcType=VARCHAR}, </if> <if test="publicity != null" > #{publicity,jdbcType=BIT}, </if> <if test="userlicenses != null" > #{userlicenses,jdbcType=INTEGER}, </if> <if test="teacherlicenses != null" > #{teacherlicenses,jdbcType=INTEGER}, </if> <if test="email != null" > #{email,jdbcType=VARCHAR}, </if> <if test="state != null" > #{state,jdbcType=BIT}, </if> <if test="messages != null" > #{messages,jdbcType=BIT}, </if> <if test="notes != null" > #{notes,jdbcType=BIT}, </if> <if test="autoRegisterPwd != null" > #{autoRegisterPwd,jdbcType=VARCHAR}, </if> <if test="language != null" > #{language,jdbcType=VARCHAR}, </if> <if test="hddMbSpace != null" > #{hddMbSpace,jdbcType=INTEGER}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="es.carlosgarcia.dao.model.Companies" > update companies <set > <if test="companykey != null" > companykey = #{companykey,jdbcType=VARCHAR}, </if> <if test="name != null" > name = #{name,jdbcType=VARCHAR}, </if> <if test="url != null" > url = #{url,jdbcType=VARCHAR}, </if> <if test="publicity != null" > publicity = #{publicity,jdbcType=BIT}, </if> <if test="userlicenses != null" > userlicenses = #{userlicenses,jdbcType=INTEGER}, </if> <if test="teacherlicenses != null" > teacherlicenses = #{teacherlicenses,jdbcType=INTEGER}, </if> <if test="email != null" > email = #{email,jdbcType=VARCHAR}, </if> <if test="state != null" > state = #{state,jdbcType=BIT}, </if> <if test="messages != null" > messages = #{messages,jdbcType=BIT}, </if> <if test="notes != null" > notes = #{notes,jdbcType=BIT}, </if> <if test="autoRegisterPwd != null" > autoRegisterPwd = #{autoRegisterPwd,jdbcType=VARCHAR}, </if> <if test="language != null" > language = #{language,jdbcType=VARCHAR}, </if> <if test="hddMbSpace != null" > hddMbSpace = #{hddMbSpace,jdbcType=INTEGER}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="es.carlosgarcia.dao.model.Companies" > update companies set companykey = #{companykey,jdbcType=VARCHAR}, name = #{name,jdbcType=VARCHAR}, url = #{url,jdbcType=VARCHAR}, publicity = #{publicity,jdbcType=BIT}, userlicenses = #{userlicenses,jdbcType=INTEGER}, teacherlicenses = #{teacherlicenses,jdbcType=INTEGER}, email = #{email,jdbcType=VARCHAR}, state = #{state,jdbcType=BIT}, messages = #{messages,jdbcType=BIT}, notes = #{notes,jdbcType=BIT}, autoRegisterPwd = #{autoRegisterPwd,jdbcType=VARCHAR}, language = #{language,jdbcType=VARCHAR}, hddMbSpace = #{hddMbSpace,jdbcType=INTEGER} where id = #{id,jdbcType=INTEGER} </update> </mapper> |
Conclusiones
Aunque este tipo de herramientas no cubrán el 100% de vuestras necesidades (por ejemplo, joins entre tablas), pueden ahorrarnos mucho tiempo y evitarnos las tareas repetitivas y desmotivadoras de los proyectos.
Bueno después de esta introducción, me despido esperando que os haya resultado de utilidad y dejándoos a vosotros profundizar en sus posibilidades y limitaciones.
Un saludo.
Carlos García.
muy bueno