💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 步骤 1 : 目前Mybatis的问题 目前分类管理中Mybatis中相关类都是自己手动编写的,包括:Category.java, CategoryMapper.java和CategoryMapper.xml。 尤其是CategoryMapper.xml里面主要是SQL语句,可以预见在接下来的开发任务中,随着业务逻辑的越来越复杂,SQL语句也会越来越复杂,进而导致开发速度降低,出错率增加,维护成本上升等问题。 为了解决手动编写SQL语句效率低这个问题,我们对Mybatis部分的代码,使用逆向工程进行重构。 所谓的逆向工程,就是在已经存在的数据库表结构基础上,通过工具,自动生成Category.java, CategoryMapper.java和CategoryMapper.xml。 ## 步骤 1 : 逆向工程工具简介 Mybatis Generator是一个用于Mybatis逆向工程的工具。 前面学习的方式都是先有pojo, mapper, xml, 然后再创建表。 用逆向工程的方式,首先保证**数据库里有表**,然后通过Mybatis Generator生成pojo, mapper和xml。 可以节约大家的时间,提高开发效率,降低出错几率 ## 步骤 2 : OverIsMergeablePlugin MybatisGenerator插件是Mybatis官方提供的,这个插件存在一个固有的Bug,即当第一次生成了CategoryMapper.xml之后,再次运行会导致CategoryMapper.xml生成重复内容,而影响正常的运行。 为了解决这个问题,需要写一个小插件类OverIsMergeablePlugin。 至于怎么使用,将在下一个步骤generatorConfig.xml讲解,这里先准备这个类。 > 不用需知道这个是内部是怎么操作的,直接拿来用,这也是从网上搜到的。 > ![](https://box.kancloud.cn/6ddfd3c3f01c9c8d731e5477cdecfbc5_278x136.png) ~~~ package com.dodoke.tmall.util; import java.lang.reflect.Field; import java.util.List; import org.mybatis.generator.api.GeneratedXmlFile; import org.mybatis.generator.api.IntrospectedTable; import org.mybatis.generator.api.PluginAdapter; public class OverIsMergeablePlugin extends PluginAdapter { @Override public boolean validate(List<String> warnings) { return true; } @Override public boolean sqlMapGenerated(GeneratedXmlFile sqlMap, IntrospectedTable introspectedTable) { try { Field field = sqlMap.getClass().getDeclaredField("isMergeable"); field.setAccessible(true); field.setBoolean(sqlMap, false); } catch (Exception e) { e.printStackTrace(); } return true; } } ~~~ ## 步骤 3 : generatorConfig.xml 在resouces下创建generatorConfig.xml文件,其目的就是为了正确使用本插件而提供必要的配置信息 1. 使用OverIsMergeablePlugin插件 `<plugin type="com.dodoke.tmall.util.OverIsMergeablePlugin" />` 2. 在生成的代码中,不提供注释。如果提供注释,生成出来的代码,看上去乱七八糟的。 `<commentGenerator>` 3. 指定链接数据库的账号和密码,既然是逆向工程,肯定要先链接到数据库才对啊 ~~~ <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost/tmall_ssm" userId="root" password=""> ~~~ 4. 指定生成的pojo,mapper, xml文件的存放位置 5. 指定表名以及表名对应的类名,这里只开放了t_category表,其他表都注释掉了,下一个知识点再讲,本知识点只讲解t_category表。 ![](https://box.kancloud.cn/c64173eb297a05ec03af1e3ac6358334_268x163.png) ~~~ <?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> <context id="DB2Tables" targetRuntime="MyBatis3"> <!--避免生成重复代码的插件--> <plugin type="com.dodoke.tmall.util.OverIsMergeablePlugin" /> <!--是否在代码中显示注释--> <commentGenerator> <property name="suppressDate" value="true" /> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库链接地址账号密码--> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost/tmall_ssm" userId="root" password=""> </jdbcConnection> <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer true,把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!--生成pojo类存放位置--> <javaModelGenerator targetPackage="com.dodoke.tmall.pojo" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <!-- 从数据库返回的值被清理前后的空格 --> <property name="trimStrings" value="true"/> </javaModelGenerator> <!--生成xml映射文件存放位置--> <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!--生成mapper类存放位置--> <javaClientGenerator type="XMLMAPPER" targetPackage="com.dodoke.tmall.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!--生成对应表及类名--> <!-- domainObjectName:生成的domain类的名字,如果不设置,直接使用表名作为domain类的名字;可以设置为somepck.domainName,那么会自动把domainName类再放到somepck包里面; enableCountByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询总条数语句(用于分页的总条数查询); enableUpdateByExample(默认true):MyBatis3Simple为false,指定是否生成动态修改语句(只修改对象中不为空的属性); enableDeleteByExample(默认true):MyBatis3Simple为false,指定是否生成动态删除语句; enableSelectByExample(默认true):MyBatis3Simple为false,指定是否生成动态查询语句; --> <!-- <table tableName="t_category" domainObjectName="Category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> 使用自增长键 <property name="my.isgen.usekeys" value="true"/> 如果设置为true,生成的model类会直接使用column本身的名字,而不会再使用驼峰命名方法,比如BORN_DATE,生成的属性名字就是BORN_DATE,而不会是bornDate <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_property" domainObjectName="Property" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_product" domainObjectName="Product" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_product_image" domainObjectName="ProductImage" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_order" domainObjectName="Order" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_property_value" domainObjectName="PropertyValue" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_review" domainObjectName="Review" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_user" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> <table tableName="t_order_item" domainObjectName="OrderItem" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="true" selectByExampleQueryId="false"> <property name="my.isgen.usekeys" value="true"/> <property name="useActualColumnNames" value="false"/> <generatedKey column="id" sqlStatement="JDBC"/> </table> --> </context> </generatorConfiguration> ~~~ ## 步骤 4 : MybatisGenerator 运行即生成pojo.mapper和xml。 > 注: 生成代码成功,只能执行一次,以后执行会覆盖掉mapper,pojo,xml 等文件上做的修改。所以在程序开始,我做了一些手脚,必须把today变量修改为今天才可以执行,这样明天再执行就无法运行了,以免以后对Category类做了改动,不小心运行了MybatisGenerator 导致Category类上做的手动改动被覆盖掉了。 ~~~ package com.dodoke.tmall.util; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.internal.DefaultShellCallback; public class MybatisGenerator { public static void main(String[] args) throws Exception { String today = "2018-08-31"; SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd"); Date now =sdf.parse(today); Date d = new Date(); if(d.getTime()>now.getTime()+1000*60*60*24){ System.err.println("——————未成成功运行——————"); System.err.println("——————未成成功运行——————"); System.err.println("本程序具有破坏作用,应该只运行一次,如果必须要再运行,需要修改today变量为今天,如:" + sdf.format(new Date())); return; } List<String> warnings = new ArrayList<String>(); boolean overwrite = true; InputStream is= MybatisGenerator.class.getClassLoader().getResource("generatorConfig.xml").openStream(); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(is); is.close(); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); myBatisGenerator.generate(null); System.out.println("生成代码成功,只能执行一次,以后执行会覆盖掉mapper,pojo,xml 等文件上做的修改"); } } ~~~ ## 步骤 5 : 自动生成的Category 这里是插件自动生成的代码,和手动编写的区别在于id类型从基本类型id变成了Integer。 ~~~ package com.dodoke.tmall.pojo; public class Category { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name == null ? null : name.trim(); } } ~~~ ## 步骤 6 : 自动生成的CategoryExample MybatisGenerator会生成一个类叫做XXXXExample的。 它的作用是进行排序,条件查询的时候使用。 在分类管理里用到了排序,但是没有使用到其条件查询,在后续的属性管理里就会看到其条件查询的用法了。 这个类比较复杂,我也没有去深入研究它是如何工作的,只是拿来使用。有兴趣的同学可以自己对每行代码进行解读。 ~~~ package com.dodoke.tmall.pojo; import java.util.ArrayList; import java.util.List; public class CategoryExample { protected String orderByClause; protected boolean distinct; protected List<Criteria> oredCriteria; public CategoryExample() { oredCriteria = new ArrayList<Criteria>(); } public void setOrderByClause(String orderByClause) { this.orderByClause = orderByClause; } public String getOrderByClause() { return orderByClause; } public void setDistinct(boolean distinct) { this.distinct = distinct; } public boolean isDistinct() { return distinct; } public List<Criteria> getOredCriteria() { return oredCriteria; } public void or(Criteria criteria) { oredCriteria.add(criteria); } public Criteria or() { Criteria criteria = createCriteriaInternal(); oredCriteria.add(criteria); return criteria; } public Criteria createCriteria() { Criteria criteria = createCriteriaInternal(); if (oredCriteria.size() == 0) { oredCriteria.add(criteria); } return criteria; } protected Criteria createCriteriaInternal() { Criteria criteria = new Criteria(); return criteria; } public void clear() { oredCriteria.clear(); orderByClause = null; distinct = false; } protected abstract static class GeneratedCriteria { protected List<Criterion> criteria; protected GeneratedCriteria() { super(); criteria = new ArrayList<Criterion>(); } public boolean isValid() { return criteria.size() > 0; } public List<Criterion> getAllCriteria() { return criteria; } public List<Criterion> getCriteria() { return criteria; } protected void addCriterion(String condition) { if (condition == null) { throw new RuntimeException("Value for condition cannot be null"); } criteria.add(new Criterion(condition)); } protected void addCriterion(String condition, Object value, String property) { if (value == null) { throw new RuntimeException("Value for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value)); } protected void addCriterion(String condition, Object value1, Object value2, String property) { if (value1 == null || value2 == null) { throw new RuntimeException("Between values for " + property + " cannot be null"); } criteria.add(new Criterion(condition, value1, value2)); } public Criteria andIdIsNull() { addCriterion("id is null"); return (Criteria) this; } public Criteria andIdIsNotNull() { addCriterion("id is not null"); return (Criteria) this; } public Criteria andIdEqualTo(Integer value) { addCriterion("id =", value, "id"); return (Criteria) this; } public Criteria andIdNotEqualTo(Integer value) { addCriterion("id <>", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThan(Integer value) { addCriterion("id >", value, "id"); return (Criteria) this; } public Criteria andIdGreaterThanOrEqualTo(Integer value) { addCriterion("id >=", value, "id"); return (Criteria) this; } public Criteria andIdLessThan(Integer value) { addCriterion("id <", value, "id"); return (Criteria) this; } public Criteria andIdLessThanOrEqualTo(Integer value) { addCriterion("id <=", value, "id"); return (Criteria) this; } public Criteria andIdIn(List<Integer> values) { addCriterion("id in", values, "id"); return (Criteria) this; } public Criteria andIdNotIn(List<Integer> values) { addCriterion("id not in", values, "id"); return (Criteria) this; } public Criteria andIdBetween(Integer value1, Integer value2) { addCriterion("id between", value1, value2, "id"); return (Criteria) this; } public Criteria andIdNotBetween(Integer value1, Integer value2) { addCriterion("id not between", value1, value2, "id"); return (Criteria) this; } public Criteria andNameIsNull() { addCriterion("name is null"); return (Criteria) this; } public Criteria andNameIsNotNull() { addCriterion("name is not null"); return (Criteria) this; } public Criteria andNameEqualTo(String value) { addCriterion("name =", value, "name"); return (Criteria) this; } public Criteria andNameNotEqualTo(String value) { addCriterion("name <>", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThan(String value) { addCriterion("name >", value, "name"); return (Criteria) this; } public Criteria andNameGreaterThanOrEqualTo(String value) { addCriterion("name >=", value, "name"); return (Criteria) this; } public Criteria andNameLessThan(String value) { addCriterion("name <", value, "name"); return (Criteria) this; } public Criteria andNameLessThanOrEqualTo(String value) { addCriterion("name <=", value, "name"); return (Criteria) this; } public Criteria andNameLike(String value) { addCriterion("name like", value, "name"); return (Criteria) this; } public Criteria andNameNotLike(String value) { addCriterion("name not like", value, "name"); return (Criteria) this; } public Criteria andNameIn(List<String> values) { addCriterion("name in", values, "name"); return (Criteria) this; } public Criteria andNameNotIn(List<String> values) { addCriterion("name not in", values, "name"); return (Criteria) this; } public Criteria andNameBetween(String value1, String value2) { addCriterion("name between", value1, value2, "name"); return (Criteria) this; } public Criteria andNameNotBetween(String value1, String value2) { addCriterion("name not between", value1, value2, "name"); return (Criteria) this; } } public static class Criteria extends GeneratedCriteria { protected Criteria() { super(); } } public static class Criterion { private String condition; private Object value; private Object secondValue; private boolean noValue; private boolean singleValue; private boolean betweenValue; private boolean listValue; private String typeHandler; public String getCondition() { return condition; } public Object getValue() { return value; } public Object getSecondValue() { return secondValue; } public boolean isNoValue() { return noValue; } public boolean isSingleValue() { return singleValue; } public boolean isBetweenValue() { return betweenValue; } public boolean isListValue() { return listValue; } public String getTypeHandler() { return typeHandler; } protected Criterion(String condition) { super(); this.condition = condition; this.typeHandler = null; this.noValue = true; } protected Criterion(String condition, Object value, String typeHandler) { super(); this.condition = condition; this.value = value; this.typeHandler = typeHandler; if (value instanceof List<?>) { this.listValue = true; } else { this.singleValue = true; } } protected Criterion(String condition, Object value) { this(condition, value, null); } protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { super(); this.condition = condition; this.value = value; this.secondValue = secondValue; this.typeHandler = typeHandler; this.betweenValue = true; } protected Criterion(String condition, Object value, Object secondValue) { this(condition, value, secondValue, null); } } } ~~~ ## 步骤 7 : 自动生成的CategoryMapper.xml 这是插件自动生成的xml,与我们自己手动写的也差不了多少,主要区别在于提供了一个 id="Example_Where_Clause"的SQL,借助这个可以进行多条件查询。 同样的我也没有去深入研究它是如何工作的,只是拿来使用。有兴趣的同学可以自己进行解读。 ~~~ <?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="com.dodoke.tmall.mapper.CategoryMapper"> <resultMap id="BaseResultMap" type="com.dodoke.tmall.pojo.Category"> <id column="id" jdbcType="INTEGER" property="id" /> <result column="name" jdbcType="VARCHAR" property="name" /> </resultMap> <sql id="Example_Where_Clause"> <where> <foreach collection="oredCriteria" item="criteria" separator="or"> <if test="criteria.valid"> <trim prefix="(" prefixOverrides="and" suffix=")"> <foreach collection="criteria.criteria" item="criterion"> <choose> <when test="criterion.noValue"> and ${criterion.condition} </when> <when test="criterion.singleValue"> and ${criterion.condition} #{criterion.value} </when> <when test="criterion.betweenValue"> and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} </when> <when test="criterion.listValue"> and ${criterion.condition} <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=","> #{listItem} </foreach> </when> </choose> </foreach> </trim> </if> </foreach> </where> </sql> <sql id="Base_Column_List"> id, name </sql> <select id="selectByExample" parameterType="com.dodoke.tmall.pojo.CategoryExample" resultMap="BaseResultMap"> select <if test="distinct"> distinct </if> 'false' as QUERYID, <include refid="Base_Column_List" /> from t_category <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> <if test="orderByClause != null"> order by ${orderByClause} </if> </select> <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from t_category where id = #{id,jdbcType=INTEGER} </select> <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer"> delete from t_category where id = #{id,jdbcType=INTEGER} </delete> <insert id="insert" keyColumn="id" keyProperty="id" parameterType="com.dodoke.tmall.pojo.Category" useGeneratedKeys="true"> insert into t_category (name) values (#{name,jdbcType=VARCHAR}) </insert> <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="com.dodoke.tmall.pojo.Category" useGeneratedKeys="true"> insert into t_category <trim prefix="(" suffix=")" suffixOverrides=","> <if test="name != null"> name, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> </trim> </insert> <update id="updateByPrimaryKeySelective" parameterType="com.dodoke.tmall.pojo.Category"> update t_category <set> <if test="name != null"> name = #{name,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> <update id="updateByPrimaryKey" parameterType="com.dodoke.tmall.pojo.Category"> update t_category set name = #{name,jdbcType=VARCHAR} where id = #{id,jdbcType=INTEGER} </update> </mapper> ~~~ > 生成的xml是比较晦涩的,感兴趣的同学,百度搜索这些用法。 ## 步骤 8 : 自动生成的CategoryMapper 与手动编写的CategoryMapper比起来,CategoryMapper也是提供CURD一套,不过方法名发生了变化,比如: delete叫做deleteByPrimaryKey, update叫做updateByPrimaryKey. 除此之外,修改还提供了一个updateByPrimaryKeySelective,其作用是只修改变化了的字段,未变化的字段就不修改了。 还有个改动是查询list,变成了`selectByExample(CategoryExample example); `这个方法的用法在紧接着的修改CategoryServiceImpl 中有演示 ~~~ package com.dodoke.tmall.mapper; import com.dodoke.tmall.pojo.Category; import com.dodoke.tmall.pojo.CategoryExample; import java.util.List; public interface CategoryMapper { int deleteByPrimaryKey(Integer id); int insert(Category record); int insertSelective(Category record); List<Category> selectByExample(CategoryExample example); Category selectByPrimaryKey(Integer id); int updateByPrimaryKeySelective(Category record); int updateByPrimaryKey(Category record); } ~~~ ## 步骤 9 : CategoryService没变化 CategoryService没变化,略过不表 ## 步骤 10 : 修改CategoryServiceImpl 因为CategoryMapper的方法名发生了变化,所以CategoryServiceImpl要做相应的调整。 值得一提的是list方法: ~~~ CategoryExample example =new CategoryExample(); example.setOrderByClause("id desc"); return categoryMapper.selectByExample(example); ~~~ 按照这种写法,传递一个example对象,这个对象指定按照id倒排序来查询 ~~~ package com.dodoke.tmall.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.dodoke.tmall.mapper.CategoryMapper; import com.dodoke.tmall.pojo.Category; import com.dodoke.tmall.pojo.CategoryExample; import com.dodoke.tmall.service.CategoryService; @Service public class CategoryServiceImpl implements CategoryService { @Autowired CategoryMapper categoryMapper; @Override public List<Category> list() { CategoryExample example = new CategoryExample(); example.setOrderByClause("id desc"); return categoryMapper.selectByExample(example); } @Override public void add(Category category) { categoryMapper.insert(category); } @Override public void delete(int id) { categoryMapper.deleteByPrimaryKey(id); } @Override public Category get(int id) { return categoryMapper.selectByPrimaryKey(id); } @Override public void update(Category category) { categoryMapper.updateByPrimaryKeySelective(category); } } ~~~ ## 步骤 11 : CategoryController没变化 CategoryController没变化,略过不表 ## 步骤 12 : 可运行项目 重构不会影响功能性,所以重构之后的代码一样可以实现分类管理的功能。 鉴于重构经历了较多的步骤,并且略微复杂,任何一步出了问题都会导致目前项目运行失败,实在自己搞不出来,拿到本章节对应的项目,解压出来比较一下。 ## 步骤 13 : 改动效果 按照这种方式修改之后,分类的pojo,xml.mapper都不用自己写的,大大提高了生产效率。 在下一个知识点也会演示其余8个实体类自动生成,一下子对应的pojo,xml,mapper都好了,生成出来的代码可读性也很好,还不会出错,生产效率简直提高得不要不要的。