Mybatis自动生成中的XXXExample

Mybati自动生成XXXExample类说明

Mybatis-generator会为每个字段产生Criterion,为底层的mapper.xml创建动态sql。如果表的字段比较多,产生的example类会十分庞大。

理论上通过example类可以构造你想到的任何筛选条件。在mybatis-generator中加以配置,配置数据表的生成操作就可以自动生成example了。

1、XXXExample结构

所以直接去看一下类结构:

public class ReviewContentExample {

    /**
     * 升序还是降序,可以连接多个。因为只会有一个,所以直接在这里连接了
     * eg: 字段+空格+asc(desc)
     */
    protected String orderByClause;

    /**
     * 去除重复,因为只会有一个,所以直接在这里设置
     *
     * true是选择不重复记录
     */
    protected boolean distinct;

    /**
     * 自定义查询条件
     * Criteria的集合,集合中对象是由or连接
     */
    protected List<Criteria> oredCriteria;

    /**
     * 将oredCriteria中的条件按照or连接起来
     */
    public ReviewContentExample() {
        oredCriteria = new ArrayList<>();
    }
  .........
}

2、GeneratedCriteria类

mybatis提供的模板类:

/**
* 自动生成的模板类
*/
protected abstract static class GeneratedCriteria {
  
  // 针对的是一个criteria中保存的条件
  protected List<Criterion> criteria;

  // 构造函数,创建条件表示当前where中的条件是什么
  protected GeneratedCriteria() {
    super();
    criteria = new ArrayList<>();
  }

  // 校验是否有条件,没有条件判断的,就是非法的,也就是没有条件
  // 如果有条件,就是和法的
  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 andIdIsNotNull() {
            addCriterion("id is not null");
            return (Criteria) this;
        }

        public Criteria andIdEqualTo(Integer value) {
            addCriterion("id =", value, "id");
            return (Criteria) this;
        }
  .........
}

下面来看一下添加条件的底层源码,针对单个的判断:

public Criteria andIdIsNotNull() {
  addCriterion("id is not null");
  // 返回自身,自身
  return (Criteria) this;
}
protected void addCriterion(String condition) {
  if (condition == null) {
    throw new RuntimeException("Value for condition cannot be null");
  }
  criteria.add(new Criterion(condition));
}

对于某一个单一条件来说,这里需要来进行设置:

protected Criterion(String condition) {
  super();
  // 设置条件
  this.condition = condition;
  // 没有对应的类型处理器
  this.typeHandler = null;
  // 没有值设置为true
  this.noValue = true;
}

看一下有值的:

public Criteria andIdEqualTo(Integer value) {
  addCriterion("id =", value, "id");
  return (Criteria) this;
}

加了条件的判断

public Criteria andIdEqualTo(Integer value) {
  // 外界给的值
  addCriterion("id =", value, "id");
  return (Criteria) this;
}
protected void addCriterion(String condition, Object value, String property) {
  // 首先对value来进行判断
  if (value == null) {
    throw new RuntimeException("Value for " + property + " cannot be null");
  }
  criteria.add(new Criterion(condition, value));
}

将当前条件和对应的值设置到条件集合中来

protected Criterion(String condition, Object value, String typeHandler) {
  super();
  
  // 设置条件
  this.condition = condition;
  
  // 设置值
  this.value = value;
  
  // 设置类型处理器,默认为null
  this.typeHandler = typeHandler;
  
  // 判断对应的值只不是集合类型
  if (value instanceof List<?>) {
    // 集合值
    this.listValue = true;
  } else {
    // 单值
    this.singleValue = true;
  }
}

3、Criterion

这个类是针对每个条件来判断,某个条件中是否给了值,如果没有值,那么表示的是静态SQL;如果给了值,表示的是动态SQL。

针对于动态SQL来说,需要进行判断

  • 1、是否是集合
  • 2、是否是between这种类型的值;
  • 3、是否是集合
public static class Criterion {
  
  // 数据库中对应的条件,如:id = 
  private String condition;

  // 条件对应的判断的值,如:id = 2,这个2就是值
  private Object value;

  // 针对的是between...and...情况
  private Object secondValue;

  // 没有值,如:id is not null
  private boolean noValue;
	
  // 是否是单值。如果是
  private boolean singleValue;

  // 是否是between
  private boolean betweenValue;

  // value是否是list集合
  private boolean listValue;

  // 对应的类型转换器是哪个类型的
  private String typeHandler;
  
  ......
    
}

4、动态SQL分析

1、首先对集合来做判断,是否有多个元素,每个元素之间利用or来进行链接

2、针对集合中的某个条件,判断是否符合条件,也就是对valid字段来做校验。如果符合条件的话,那么遍历集合中的每个字段对应的条件。

【注意】:注意这里的遍历条件,因为是按照for循环添加的顺序来进行遍历的,所以必须考虑

 <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>

注意test中的判断,判断的是对象中的属性判断信息。

1、首先对最外层的结合判断,使用or来进行拼接;

2、然后是对每个字段中的判断条件来进行选择;

3、最终来对如果要是集合中的数据来进行使用的话,可以选择对应的使用方式。

再来看一些:

<select id="selectByExample" parameterType="com.turing.draw.model.dto.DrawTaskExample" resultMap="BaseResultMap">
  <!--@mbg.generated-->
  select
  <if test="distinct">
    distinct
  </if>
  <include refid="Base_Column_List" />
  from draw_task
  <if test="_parameter != null">
    <include refid="Example_Where_Clause" />
  </if>
  <if test="orderByClause != null">
    order by ${orderByClause}
  </if>
</select>

最终会将这里的orderByClause的值拷贝出来,所以在这里,如果需要多个条件的话,那么需要来写对应的条件即可。

如以下判断语句:

order by id desc , name asc

看到这样一行判断:

TaskRecordExample recordExample = new TaskRecordExample();
recordExample.setOrderByClause(" created_time desc");
TaskRecordExample.Criteria criteria = recordExample.createCriteria();

criteria.andIsDelEqualTo(0);

if (!StringUtils.isEmpty(searchVO.getSearchName())) {
  criteria.andTaskNameLike("%" + searchVO.getSearchName() + "%");
}

if (!StringUtils.isEmpty(searchVO.getTemplateId())) {
  criteria.andTemplateIdEqualTo(searchVO.getTemplateId());
}

利用这种if判断的方式来进行书写,虽然代码上不美观,但是很少用。

在这里看到一行SQL语句:

<select id="selectByExample" resultMap="BaseResultMap" parameterType="com.turing.procedure.model.dto.task.TaskRecordExample" >
  select
  <if test="distinct" >
    distinct
  </if>
  'true' as QUERYID,
  <include refid="Base_Column_List" />
  from task_record
  <if test="_parameter != null" >
    <include refid="Example_Where_Clause" />
  </if>
  <if test="orderByClause != null" >
    order by ${orderByClause}
  </if>
</select>

这里可以看到一个比较奇葩的功能,如果没有去重条件的话,那么这里将会是select from ,中间什么语句也没有。

但是这里为了防止出现这种情况,所以使用了一种类似where 1 = 1的情况来进行使用。

这里运用到了ObjectMetadata来进行解析。

上面的判断是比较简单的使用方式了。

        // 审核内容查询
        ReviewContentExample contentExample = new ReviewContentExample();
        ReviewContentExample.Criteria criteria = contentExample.createCriteria();
        criteria.andTaskSnEqualTo(taskSn);
        criteria.andVersionEqualTo(version);

        //子场景要素判断
        if (Tools.isNotEmpty(selectElements)) {
            criteria.andEleIdIn(selectElements);
        }
ForAnalyzeExample queryI = new ForAnalyzeExample();
queryI.createCriteria()
  .andTaskSnEqualTo( cTaskSn )
  .andEleIdEqualTo( cEleId )
  .andParentTaskEqualTo( cParentTaskSn )
  .andVersionEqualTo( -1 );
List< ForAnalyze > oriVs = forAnalyzeMapper.selectByExample( queryI );

下面来看下各种使用方式

int deleteByExample( DrawTaskExample example );

5、总结

判断是查询,还是增伤改,只需要看里面对应的SQL语句即可。

其实本质上还是需要对各种条件的判断熟烂于心,不懂的话可以去看一下源码。这样子来写代码事半功倍。

posted @ 2022-08-31 22:10  写的代码很烂  阅读(1423)  评论(0编辑  收藏  举报