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("lidaye@123.com");
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), wangwu@123.com(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);

mybatis中的土鸡杂鱼的更多相关文章

  1. [原创]关于mybatis中一级缓存和二级缓存的简单介绍

    关于mybatis中一级缓存和二级缓存的简单介绍 mybatis的一级缓存: MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候 ...

  2. 记录一次bug解决过程:mybatis中$和#的使用

    一.总结 mybatis中使用sqlMap进行sql查询时,经常需要动态传递参数.动态SQL是mybatis的强大特性之一,也是它优于其他ORM框架的一个重要原因.mybatis在对sql语句进行预编 ...

  3. mybatis中#{}与${}的差别(如何防止sql注入)

    默认情况下,使用#{}语法,MyBatis会产生PreparedStatement语句中,并且安全的设置PreparedStatement参数,这个过程中MyBatis会进行必要的安全检查和转义. # ...

  4. mybatis 中的where标签

    mybatis中的where标签可以去除 开头的 and 或者 or 但是放在后面的不行 失败的: <select id="countNotesByParam" parame ...

  5. Mybatis中SqlMapper配置的扩展与应用(3)

    隔了两周,首先回顾一下,在Mybatis中的SqlMapper配置文件中引入的几个扩展机制: 1.引入SQL配置函数,简化配置.屏蔽DB底层差异性 2.引入自定义命名空间,允许自定义语句级元素.脚本级 ...

  6. mybatis中使用使用模块化sql

    主要使用到mybatis中的标签 <sql id="tempId"> select * from student <sql> 使用的标签如下: <in ...

  7. “mybatis 中使用foreach 传

    为了帮助网友解决“mybatis 中使用foreach 传”相关的问题,中国学网通过互联网对“mybatis 中使用foreach 传”相关的解决方案进行了整理,用户详细问题包括:mybatismap ...

  8. Mybatis中的in查询和foreach标签

    Mybatis中的foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合. foreach元素的属性主要有 item,index,collection,open,separato ...

  9. mybatis中foreach的用法(转)

    foreach一共有三种类型,分别为List,[](array),Map三种. foreach属性 属性 描述 item 循环体中的具体对象.支持属性的点路径访问,如item.age,item.inf ...

  10. 6.Mybatis中的动态Sql和Sql片段(Mybatis的一个核心)

    动态Sql是Mybatis的核心,就是对我们的sql语句进行灵活的操作,他可以通过表达式,对sql语句进行判断,然后对其进行灵活的拼接和组装.可以简单的说成Mybatis中可以动态去的判断需不需要某些 ...

随机推荐

  1. Django静态文件配置、form表单、request对象、连接数据库、ORM

    目录 静态文件配置 静态文件相关配置 1.接口前缀 浏览器停用缓存 2.接口前缀动态匹配 form表单 action 控制数据提交的地址 method 控制数据提交的方法 请求方法补充 get: 朝服 ...

  2. Proxmark3 Easy 如何流畅的在Linux中操作?

    前言 Proxmark3 Easy可谓是国内能买到的比较便宜的RFID卡防设备了,曾在部分hv中出现. Proxmark3 Easy是国内简化的一种设备,利用的是较早的Proxmark3的开源,基础的 ...

  3. [OpenCV实战]41 嵌入式计算机视觉设备选择

    文章目录 1 简介 1.1 深度学习与传统计算机视觉 1.2 性能考量 1.3 社区支持 2 结论 3 参考 在计算机视觉领域中,不同的场景不同的应用程序需要不同的解决方案.在本文中,我们将快速回顾可 ...

  4. [python] NetworkX实例

    文章目录 NetworkX实例 1. 基础Basic 2. 绘图Drawing 3. 图标Graph NetworkX实例 代码下载地址 NetworkX 2.4版本的通用示例性示例.本教程介绍了约定 ...

  5. 【RocketMQ】负载均衡源码分析

    RocketMQ在集群模式下,同一个消费组内,一个消息队列同一时间只能分配给组内的某一个消费者,也就是一条消息只能被组内的一个消费者进行消费,为了合理的对消息队列进行分配,于是就有了负载均衡. 接下来 ...

  6. [WPF]将方法设为弃用

    [System.Obsolete("这是一条提示信息,表示这个方法弃用了,使用此方法会有一条Warning信息")] private void SaveDataMessage(Sa ...

  7. P1605迷宫——题解

    展开 题目背景 给定一个N*M方格的迷宫,迷宫里有T处障碍,障碍处不可通过.给定起点坐标和终点坐标,问: 每个方格最多经过1次,有多少种从起点坐标到终点坐标的方案.在迷宫中移动有上下左右四种方式,每次 ...

  8. 【Java刷题】初始化List应该选择ArrayList还是LinkedList

    文章目录 前言 题目 解题思路 遇到的坑:ArrayList和LinkedList 未通过代码 通过代码 小结 ArrayList和LinkedList的区别 在刷题的时候应该如何选择 排序效率 测试 ...

  9. JavaScript 内存管理及垃圾回收

    一.内存管理 JavaScript 是一种自动垃圾回收语言,这意味着 JavaScript 引擎会自动监测和清理无用的内存. JavaScript 中的内存管理主要由 JavaScript 引擎负责, ...

  10. Java基础学习笔记-流程控制

    Java程序结构 顺序结构 分支选择结构 循环结构 顺序结构 分支选择结构 if-else Switch case Switch case 注意点 要配合break,要不就会一直往下走 case 值必 ...