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. openresty package path

    openresty lua_package_path 是整个openresty最基础的功能,不理解 path就无法做项目,更无法写框架. 先看下文档lua_package_path https://g ...

  2. Linux系统CentOS6找回密码解决方法

    1.首先在开机启动的时候快速按键盘上的"E"键 或者"ESC"键,会进入如下界面,按E键: 2.出现下面这个界面,选择第二项以kernel开头,再次按" ...

  3. uniapp 微信小程序 引入 环信聊天

    最近项目需要实现一个聊天的功能,群聊或者单聊,用到环信,根据官网实现一下相关的配置吧 第一:下载环信demo  地址:https://github.com/easemob/webim-uniapp-d ...

  4. [python] mxnet60分钟入门Gluon教程

    mxnet60分钟入门Gluon教程代码下载,适合做过深度学习的人使用.入门教程地址: https://beta.mxnet.io/guide/getting-started/crash-course ...

  5. [OpenCV实战]26 基于OpenCV实现选择性搜索算法

    目录 1 背景 1.1 目标检测与目标识别 1.2 滑动窗口算法 1.3 候选区域选择算法 2 选择性搜索算法 2.1 什么是选择性搜索? 2.2 选择性搜索相似性度量 2.3 结果 3 代码 4 参 ...

  6. ArcGIS工具 - 批量合并数据库

    在ArcGIS数据建库和入库过程中,经常需要将数据结构相同.数据内容不同的多个数据库文件进行合并,使用工具臬中复制.合并.追加都可以达到一定的效果,但都不能直接支持数据库的操作.当合并成百上千个数据库 ...

  7. NET-Core利用etag进行浏览器缓存

    title: .NET Core浏览器缓存方案 date: 2022-12-02 14:17:36 tags: - .NET 缓存介绍及方案 在后端开发中经常会使用缓存来提高接口的响应速度,降低系统的 ...

  8. sikulix___自动化办公___重复性_机械性_的电脑操作___python脚本___Java运行环境下德jar包完成自动化测试相关___截图编程控制键盘鼠标

    sikulix___自动化办公___重复性_机械性_的电脑操作___python脚本___Java运行环境下德jar包完成自动化测试相关___截图编程控制键盘鼠标 应用场景: 公司内的大佬更改了xml ...

  9. LTC2440串行SPI通讯时序

    LTC2440 简介 我们使用4-wire SPI接口 按照时序图上的描述,SDO是在SCLK的下降沿更新数据,那么FPGA接收端就应该在上升沿采集数据. 实际测试发现SDO数据相对于SCLK延迟了6 ...

  10. Typora软件下载与markdown语法的使用

    Typora软件下载与markdown语法的使用 一.Typora下载 1.Typora的简介 Typora是一款轻量级文本编辑器,文本阅读器,是目前最火爆的文本编辑器. Typora中格式,字体,主 ...