首页 > 其他分享 >mybatis中的土鸡杂鱼

mybatis中的土鸡杂鱼

时间:2022-12-26 16:23:06浏览次数:60  
标签:mapper configuration String mappedStatement void mybatis 杂鱼 null 土鸡

mybatis中的土鸡杂鱼

目录

1、mapper接口为什么要和mapper.xml在同一个路径下?

为什么在单独学习使用mybatis的时候,强烈推荐要放在同一个路径下?

首先看下我的配置文件写法如下:

    <mappers>
        <package name="com.guang.mybatis.mapper"/>
    </mappers>

也就是说,mapper接口都是com.guang.mybatis.mapper路径下,那么我们的配置文件也要在这个路径下。

那么按照这个思路来,解析过程在XMLMapperBuilder中org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement中

  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        }
        // ....省略代码

然后来到

  public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      // 加载xml
      loadXmlResource();
      //...省略代码

那么仔细看下下面这段代码:

  private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      
      // 根据类的全限定路径找对应的xml文件
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      // #1347
      InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
      if (inputStream == null) {
        // Search XML mapper that is not in the module but in the classpath.
        try {
          inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
        } catch (IOException e2) {
          // ignore, resource is not required
        }
      }
      if (inputStream != null) {
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }

看到这里,就知道了为什么说要mapper接口和mapper.xml的路径保持一致的原因了。

2、主键生成为什么配置一个字段就可以?

若数据库支持自动生成主键(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=“true”,表明使用自增主键获取主键值策略,然后再利用 keyProperty 属性指定对应的主键属性,也就是 Mybatis 获取到主键值后,将这个值封装给 JavaBean 的哪个属性。

<insert id="addEmp" databaseId="mysql" parameterType="employee" useGeneratedKeys="true" keyProperty="id">
    insert into tbl_employee (id, last_name, email, gender)
    values (#{id}, #{lastName}, #{email}, #{gender});
</insert>

获取自增主键值:

@Test
public void test04() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try{
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
        Employee employee = new Employee();
        employee.setLastName("lig");
        employee.setEmail("[email protected]");
        employee.setGender("1");
        boolean addEmp = mapper.addEmp(employee);
        System.out.println(addEmp);
        // 直接通过 getId() 方法来获取自增主键值
        System.out.println(employee.getId());
        sqlSession.commit();
    } finally {
        sqlSession.close();
    }
}
[main][com.example.mapper.EmployeeMapper.addEmp]-[DEBUG] ==>  Preparing: insert into tbl_employee (id, last_name, email, gender) values (?, ?, ?, ?); 
[main][com.example.mapper.EmployeeMapper.addEmp]-[DEBUG] ==> Parameters: null, wangwu(String), [email protected](String), 1(String)
[main][com.example.mapper.EmployeeMapper.addEmp]-[DEBUG] <==    Updates: 1
true
9

其中“9”为获取到的自增主键值。

其他数据库厂商参考文章

原理

下面看在解析过程中org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode

	// ...省略代码
	// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
	// ...省略代码

如果在对应的增伤改查语句中写了useGeneratedKeys=true,那么这里就会开始使用Jdbc3KeyGenerator.INSTANCE

那么既然会使用到Jdbc3KeyGenerator,那么看一下在哪里执行的。

首先追踪到org.apache.ibatis.executor.statement.BaseStatementHandler#BaseStatementHandler

    if (boundSql == null) { // issue #435, get the key before calculating the statement
      generateKeys(parameterObject);
      boundSql = mappedStatement.getBoundSql(parameterObject);
    }
  protected void generateKeys(Object parameter) {
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    ErrorContext.instance().store();
    keyGenerator.processBefore(executor, mappedStatement, null, parameter);
    ErrorContext.instance().recall();
  }

但是发现org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#processBefore中什么都没有做

然后在org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement

  protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {      
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }

关于这里的KeyColumns参考文章:https://www.cnblogs.com/woyujiezhen/p/11643944.html

但是在MySQL中,我们并没有配置这个东西。所以这里仅仅只是创建了一个预编译的PreparedStatement,并且设置是有返回生成的主键的。

然后找到了org.apache.ibatis.executor.statement.PreparedStatementHandler#update

  public int update(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    int rows = ps.getUpdateCount();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
    return rows;
  }

具体的就可以看下org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator#processBatch

  public void processBatch(MappedStatement ms, Statement stmt, Object parameter) {
    // 首先获取得到配置的keyProperty的值,最终会在assignKeys中将方法进行赋值进去
    final String[] keyProperties = ms.getKeyProperties();
    if (keyProperties == null || keyProperties.length == 0) {
      return;
    }
    try (ResultSet rs = stmt.getGeneratedKeys()) {
      final ResultSetMetaData rsmd = rs.getMetaData();
      final Configuration configuration = ms.getConfiguration();
      if (rsmd.getColumnCount() < keyProperties.length) {
        // Error?
      } else {
        assignKeys(configuration, rs, rsmd, keyProperties, parameter);
      }
    } catch (Exception e) {
      throw new ExecutorException("Error getting generated key or setting result to parameter object. Cause: " + e, e);
    }
  }

3、为什么默认使用的是预编译PreparedStatement?

这里就和上面一样的位置:

StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));

4、分页对象RowBounds和ResultContext

在org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap方法中有一个细节的地方

  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    // 默认结果集处理
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    // 跳过指定行
    skipRows(resultSet, rowBounds);
    // 利用ResultContext来进行选择是否停止
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

其实本质上的原因,我们不会用到这里的东西。因为都是默认的,但是如果使用了Sqlsession提供的API中包含了有RowBounds和ResultHandler,那么我们可以来实现自定义处理。

根据官方描述是为了防止数据量太大的情况,但是我们一般来说,只要SQL写的好,就能够避免掉这种情况。

<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);

// 因为做了自定义处理,所以结果集应该另外选择了一个集合保存。故返回值为void
void select(String statement, Object parameter, ResultHandler handler);

标签:mapper,configuration,String,mappedStatement,void,mybatis,杂鱼,null,土鸡
From: https://www.cnblogs.com/likeguang/p/17006082.html

相关文章

  • 数据库悲观锁和乐观锁使用Mybatis
    一下是转载的oracle和Mysql两种数据库悲观锁和乐观锁机制及乐观锁实现方式:一、OracleOracle数据库悲观锁与乐观锁是本文我们主要要介绍的内容。有时候为了得到最大的性能,一......
  • Mybatis精讲课程详情
    Mybatis精讲课程详情一.课程介绍本套视频课程为 IT私塾 出品的《跟一一哥学Java》系列课程之Mybatis精讲课程。分类名称操作系统无要求环境要求JDK1.7+,MySQL5.5+注意:本课......
  • Java开发学习(四十七)----MyBatisPlus删除语句之多记录操作
    1、多记录操作先来看下问题:之前添加了很多商品到购物车,过了几天发现这些东西又不想要了,该怎么办呢?很简单删除掉,但是一个个删除的话还是比较慢和费事的,所以一般会给用......
  • Java反射详解,学以致用,实战案例(AOP修改参数、Mybatis拦截器实现自动填充)
    持续创作,加速成长!这是我参与「掘金日新计划·10月更文挑战」的第1天,点击查看活动详情作为Java开发者,你认为反射这个知识点重要程度,在你心里是什么样的呢?以前我也只觉......
  • 【框架】MybatisPlus
    开篇MyBatiesPlus(简称MP)是基于MyBatis框架基础上开发的增强工具,旨在简化开发,、提高效率。开发方式基于MyBatis使用MyBatisPlus基于Spring整合MyBatisPlus基于S......
  • MybatisPlus 中文入库变成问号
    环境依赖pom <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency......
  • mybatis拦截器 + 自定义注解
    背景:前两天写过一篇关于:mybatis拦截器+自定义注解+获取注解的属性的文章,感觉写得不是很好,有很多事情没有说明清楚包括:mybatis拦截器的@Signature注解的几个参数......
  • mybatis拦截器 + 自定义注解 + 获取注解的属性
    背景mybatis拦截器+自定义注解——这种方式可以为我们解决很多事情,带来很多便利,但有时候会在自定义注解上配置一些属性,并且拦截器上要拿到这些属性的值。这个时候,我们......
  • ”一个馒头引发的血案“|记Mybatis之BindingException异常的产生及解决过程
    一.业务场景前几天壹哥带学生做一个项目,需要更新数据库中的车辆信息表,具体需求是要根据指定车辆的设备id(编号和设备ID均非主键)来更新车辆信息。壹哥要求学生们用Mybatis......
  • 异常解决!Mybatis实体类属性名和表中字段名不一致的处理方案
    一.前言最近壹哥有学生出去面试,被问到“Mybatis实体类的属性名和表中的字段名不一致该怎么处理?”,这其实是一个很经典的面试题,接下来壹哥就为大家详细解析一下这道面试题。......