mybatis

2021/5/5 18:28:28

本文主要是介绍mybatis,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Mybatis

1、Mybatis是一个半ORM(对象关系映射)持久层框架,它内部封装了JDBC,开发时只需要关注SQL语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接编写原生态sql,可以严格控制sql执行性能,灵活度高。

持久化:程序的数据瞬时状态(内存中,断电即失)==》持久态(数据库中、文件中)

2、通过xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过java对象和 statement中sql的动态参数进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射为java对象并返回。

 

3、优点:

SQL语句编程,相当灵活,sql和代码分离

与Spring很好的集成

提供映射标签,支持对象与数据库的ORM字段关系映射

与各种数据库兼容

简化jdbc代码

4、不足:

依赖数据库,不能随便更换数据库

需要编写sql,有编写sql的能力

 

5、#{}和${}的区别是什么?

#{} 是预编译处理,${}是字符串替换。

Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;

Mybatis在处理${}时,就是把${}替换成变量的值。

使用#{}可以有效的防止SQL注入,提高系统安全性。

6、当实体类中的属性名和表中的字段名不一样  ?

第1种: 通过在查询的sql语句中定义字段名的别名,让字段名的别名和实体类的属性名一致。

第2种: 通过 <resultMap> 来映射字段名和实体类属性名的一一对应的关系

7、Mapper 接口的工作原理

是JDK动态代理,Mybatis运行时会使用JDK动态代理为Mapper接口生成代理对象proxy,代理对象会拦截接口方法,转而执行MapperStatement所代表的sql,然后将sql执行结果返回。

 

8、Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式

第一种是使用 <resultMap> 标签,逐一定义数据库列名和对象属性名之间的映射关系。

第二种是使用sql列的别名功能,将列的别名书写为对象属性名。

有了列名与属性名的映射关系后,Mybatis通过反射创建对象,同时使用反射给对象的属性逐一赋值并返回,那些找不到映射关系的属性,是无法完成赋值的。

9、在mapper中如何传递多个参数

1、第一种:

DAO层的函数

2、第二种: 使用 @param 注解:

然后,就可以在xml像下面这样使用(推荐封装为一个map,作为单个参数传递给mapper):

3、第三种:多个参数封装成map

10、Mybatis动态sql有什么用?执行原理?有哪些动态sql?

Mybatis动态sql可以在Xml映射文件内,以标签的形式编写动态sql,执行原理是根据表达式的值 完成逻辑判断并动态拼接sql的功能。

Mybatis提供了9种动态sql标签: trim|where|set|foreach|if|choose|when|otherwise|bind。

11、Mybatis是否支持延迟加载

Mybatis仅支持association关联对象和collection关联集合对象的延迟加载,association指的就是一对一,collection指的就是一对多查询。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=true|false

原理:使用CGLIB创建目标对象的代理对象,当调用目标方法时,进入拦截器方法

 

 

 

 

12、工作原理:

SqlSessionFactoryBuilder从mybatis-config.xml配置文件中构建出SqlSessionFactory

SqlSessionFactory 文开启一个SqlSession,实例获得Mapper对象并运行mapper映射的SQl语句。完成数据库的crud操作和事务提交,关闭SqlSession

 

1)读取 MyBatis 配置文件;mybatis-config.xml

2)加载映射文件:SQL 映射文件

3)构造会话工厂:构建会话工厂 SqlSessionFactory

4)创建会话对象:建 SqlSession 对象,包含执行 SQL 语句的所有方法

5)Executor 执行器:Executor 接口来操作数据库,根据 SqlSession 传递的参数动态地生成要执行的 SQL 语句

6)MappedStatement 对象:存储要映射的 SQL 语句的 id、参数等信息

7)输入参数映射: Map、 List 等集合类型,也可以基本数据类型和 POJO 类型

8)输出结果映射: Map、 List 等集合类型,也可以基本数据类型和 POJO 类型

 

13、Mybatis分页原理:

通过我们设置的RowBounds来返回分页结果的

原理:

将所有结果查询出来,然后通过计算offset和limit,只返回部分结果,操作在内存中进行,所以也叫内存分页

缺点:数据量大,不行

物理分页:

直接在SQL加limit

 

14、MyBatis编程步骤是什么样的?

创建SqlSessionFactory——>创建SqlSession ——>执行数据库操作——> session.commit()提交事务——> session.close()关闭会话

 

 

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例

通过sqlswssionfactory获得sqlsession,sqlsession提供了在数据库执行sql命令所有方法,通过sqlsession直接执行已经映射的SQL语句

mybatis-config.xml

environment 元素体中包含了事务管理和连接池的配置

命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了接口绑定。

 

XML方式

 

<mapper namespace="org.mybatis.example.BlogMapper">

  <select id="selectBlog" resultType="Blog">

    select * from Blog where id = #{id}

  </select></mapper>

注解方式:适用于简单的SQL

public interface BlogMapper {

  @Select("SELECT * FROM blog WHERE id = #{id}")

  Blog selectBlog(int id);}

SqlSessionFactoryBuilder类

可以实例化、使用和丢弃,创建了SqlSessionFactory之后,不再需要,作用域:方法作用域,局部方法变量,利用它创建多个SqlSessionFactory,然后释放。

 

SqlSessionFactoryBuilder 有五个 build() 方法,每一种都允许你从不同的资源中创建一个 SqlSessionFactory 实例

 

SqlSessionFactory类

一旦创建,运行期间,一直存在,不要多次重建,作用域:应用作用域,多种实现:

最简单的就是使用单例模式或者静态单例模式。

 

String resource = "org/mybatis/example/mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSessionFactory 有六个方法创建 SqlSession 实例。

 

 

SqlSession

每个线程都有自己的SqlSession实例,线程不安全,不能共享,作用域:请求或方法作用域,不能放在类的静态域或者类的实例变量,每次收到HTTP请求 ,就打开一个SqlSession,返回响应,就关闭,放到finally中。

SqlSession提供select/insert/update/delete方法,在旧版本中使用使用SqlSession接口的这些方法,但是新版的Mybatis中建议使用Mapper接口的方法,底层还是采用SqlSession接口方法实现的

 

执行包含了所有执行语句、提交或回滚事务以及获取映射器实例的方法。

 

通过这个接口来执行命令,获取映射器示例和管理事务

try (SqlSession session = sqlSessionFactory.openSession()) {

  // 应用逻辑代码}

映射器实例

映射器其实就是一个动态代理对象

映射器接口的实例是从 SqlSession 中获得的

 

configuration(配置)

  • properties(属性)
  • settings(设置)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境配置)
    • environment(环境变量)
      • transactionManager(事务管理器)
      • dataSource(数据源)
  • databaseIdProvider(数据库厂商标识)
  • mappers(映射器)

加载顺序:

如果一个属性在不只一个地方进行了配置,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。

MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。

<property name="username" value="${username:ut_user}"/>

这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。

<properties resource="org/mybatis/example/config.properties">

  <!-- ... -->

  <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 --></properties>

设置(settings)

<settings>

  <setting name="cacheEnabled" value="true"/>  //开启缓存

  <setting name="lazyLoadingEnabled" value="true"/> //开启延迟加载

  <setting name="multipleResultSetsEnabled" value="true"/>//允许单个语句返回多结果集

  <setting name="useColumnLabel" value="true"/>

  <setting name="useGeneratedKeys" value="false"/>//允许 JDBC 支持自动生成主键

  <setting name="autoMappingBehavior" value="PARTIAL"/>

  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>

  <setting name="defaultExecutorType" value="SIMPLE"/>

  <setting name="defaultStatementTimeout" value="25"/>//设置超时时间

  <setting name="defaultFetchSize" value="100"/>

  <setting name="safeRowBoundsEnabled" value="false"/>

  <setting name="mapUnderscoreToCamelCase" value="false"/>//是否开启驼峰命名自动映射

  <setting name="localCacheScope" value="SESSION"/>

  <setting name="jdbcTypeForNull" value="OTHER"/>

  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/></settings>

 

类型别名(typeAliases)

<typeAliases>

  <typeAlias alias="Author" type="domain.blog.Author"/>

  <typeAlias alias="Blog" type="domain.blog.Blog"/>

  <typeAlias alias="Tag" type="domain.blog.Tag"/></typeAliases>

 

别名包 :默认是bean的小写,大写也可以

<typeAliases>

        <package name=”com.wang.entity”></typeAliases>

注解的别名优先级更高

@Alias(“hello”)  实体类上

类型处理器(typeHandlers)

得知该类型处理器处理的 Java 类型

  • 在类型处理器的配置元素(typeHandler 元素)上增加一个 javaType 属性(比如:javaType="String");
  • 在类型处理器的类上增加一个 @MappedTypes 注解指定与其关联的 Java 类型列表。 如果在 javaType 属性中也同时指定,则注解上的配置将被忽略。

通过两种方式来指定关联的 JDBC 类型

  • 在类型处理器的配置元素上增加一个 jdbcType 属性(比如:jdbcType="VARCHAR");
  • 在类型处理器的类上增加一个 @MappedJdbcTypes 注解指定与其关联的 JDBC 类型列表。 如果在 jdbcType 属性中也同时指定,则注解上的配置将被忽略

对象工厂(objectFactory)

每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。

插件(plugins)使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

环境配置(environments)

可以配置多个环境,每个 SqlSessionFactory 实例只能选择一种环境。

所以,如果想连接两个数据库,就需要创建两个 SqlSessionFactory 实例。

 

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
    • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。一些容器并不希望连接被关闭,需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。

<transactionManager type="MANAGED">

  <property name="closeConnection" value="false"/></transactionManager>

使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

数据源(dataSource)

三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")

UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用

可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory 来使用第三方数据源实现

 

映射器(mappers)

直接告诉 MyBatis 到哪里去找映射文件。 可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:///形式的 URL),或类名和包名等。

<!-- 使用相对于类路径的资源引用 -->

<mappers>

  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>

  <mapper resource="org/mybatis/builder/PostMapper.xml"/></mappers>

<!-- 使用完全限定资源定位符(URL) --><mappers>

  <mapper url="file:///var/mappers/AuthorMapper.xml"/>

  <mapper url="file:///var/mappers/PostMapper.xml"/></mappers>

<!-- 使用映射器接口实现类的完全限定类名 --><mappers>

  <mapper class="org.mybatis.builder.AuthorMapper"/>

  <mapper class="org.mybatis.builder.PostMapper"/></mappers>

<!-- 将包内的映射器接口实现全部注册为映射器 --><mappers>

  <package name="org.mybatis.builder"/></mappers>

XML 映射器

<select

  id="selectPerson"     //唯一标识

  parameterType="int"  //传入语句的参数的类的全限定名或别名 ,可选通过类型处理器推断。

  parameterMap="deprecated"//废弃,使用行内参数映射和parameter Type代替

  resultType="hashmap"  //希望返回的类的全限定名或别名,返回集合,则返回集合包含的类型,

不是结合本身,和resultMap只能有一个

  resultMap="personResultMap"//外部resultmap的引用,最强大的特性

  flushCache="false"// 设置为true ,语句被调用,会导致本地缓存和二级缓存被清空

  useCache="true"//语句结果会被二级缓存缓存起来

  timeout="10"//超时时间,抛异常之前,程序等待数据库相应的时间,默认:unset未设置

  fetchSize="256"//建议值,让驱动程序每次返回结果的行数等于这个建议值

  statementType="PREPARED"//STATEMENT,PREPARED 或 CALLABLE,默认值PREPARED,

  resultSetType="FORWARD_ONLY">

useGeneratedKeys

(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法

来取出由数据库内部生成的主键

(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段)

,默认值:false。

keyProperty

(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用

 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,

默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。

如果数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性就可以。例如,

<insert id="insertAuthor" useGeneratedKeys="true"

    keyProperty="id">

  insert into Author (username,password,email,bio)

  values (#{username},#{password},#{email},#{bio})</insert>

数据库还支持多行插入, 你也可以传入一个 Author 数组或集合,并返回自动生成的主键。

<insert id="insertAuthor" useGeneratedKeys="true"

    keyProperty="id">

  insert into Author (username, password, email, bio) values

  <foreach item="item" collection="list" separator=",">

    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})

  </foreach></insert>

字符串替换

默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。 这样做更安全,更迅速,通常也是首选做法,不过有时就是想直接在 SQL 语句中直接插入一个不转义的字符串。 比如 ORDER BY 子句,这时候可以

ORDER BY ${columnName}

如果想 select 一个表任意一列的数据时

@Select("select * from user where ${column} = #{value}")User findByColumn(@Param("column") String column, @Param("value") String value);

其中 ${column} 会被直接替换,而 #{value} 会使用 ? 预处理

结果映射

连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系。

一个javabean 三个属性

id,username 和 hashedPassword。映射resultset

<!-- mybatis-config.xml 中 --><typeAlias type="com.someapp.model.User" alias="User"/>

<!-- SQL 映射 XML 中 --><select id="selectUsers" resultType="User">

  select id, username, hashedPassword

  from some_table

  where id = #{id}</select>

MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配

<select id="selectUsers" resultType="User">

  select

    user_id             as "id",

    user_name           as "userName",

    hashed_password     as "hashedPassword"

  from some_table

  where id = #{id}</select>

解决列名不匹配:上面的隐式配置,在sql语句给列名起一个别名

解决列名不匹配的另外一种方式:需要显式的配置resultmap

 

<resultMap id="userResultMap" type="User">

  <id property="id" column="user_id" />

  <result property="username" column="user_name"/>

  <result property="password" column="hashed_password"/></resultMap>

<select id="selectUsers" resultMap="userResultMap">

  select user_id, user_name, hashed_password

  from some_table

  where id = #{id}</select

 

高级结果映射

数据库不可能永远是你所想或所需的那个样子,并不都是每个数据库都具备良好的第三范式或 BCNF 范式,一种数据库映射模式,完美适配所有的应用程序,ResultMap 就是 MyBatis 对这个问题的答案。

 

结果映射(resultMap)

constructor - 用于在实例化类时,注入结果到构造方法中

idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能

arg - 将被注入到构造方法的一个普通结果

id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

result – 注入到字段或 JavaBean 属性的普通结果

association – 一个复杂类型的关联;许多结果将包装成这种类型

嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用

collection – 一个复杂类型的集合

嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用

discriminator – 使用结果值来决定使用哪个 resultMap

case – 基于某些值的结果映射

嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

Id 和 Result 的属性

属性

描述

property

映射到列结果的字段或属性。 JavaBean 有这个名字的属性(property),

先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。

 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。

可以这样映射简单的东西:“username”,或者映射到复杂的东西:“address.street.number”。

column

数据库中的列名,或别名。和传递给 resultSet.getString(columnName) 方法的参数一样。

javaType

一个 Java 类的全限定名,或一个类型别名(如果你映射到一个 JavaBean

MyBatis 通常可以推断类型。

如果你映射到的是 HashMap,你应该明确地指定 javaType 来保证行为与期望的相一致。

jdbcType

JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。

在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。

这是 JDBC 的要求而非 MyBatis 的要求。

直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。

typeHandler

默认的类型处理器。使用这个属性,可以覆盖默认的类型处理器。

这个属性值是一个类型处理器实现类的全限定名,或者是类型别名

 

 

 

 构造方法注入允许你在初始化时为类设置属性的值,而不用暴露出公有方法。

关联

<association property="author" column="blog_author_id" javaType="Author">

  <id property="id" column="author_id"/>

  <result property="username" column="author_username"/></association>

MyBatis 有两种不同的方式加载关联:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集

javaType

一个 Java 类的完全限定名,或一个类型别名,如果你映射到一个 JavaBean,

MyBatis 通常可以推断类型。如果映射到的是 HashMap,

应该明确地指定 javaType 来保证行为与期望的相一致。

关联的嵌套 Select 查询

示例:

我们有两个 select 查询语句:一个用来加载博客(Blog),另外一个用来加载作者(Author),而且博客的结果映射描述了应该使用 selectAuthor 语句加载它的 author 属性,其它所有的属性将会被自动加载,只要它们的列名和属性名相匹配。

<resultMap id="blogResult" type="Blog">

  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/></resultMap>

<select id="selectBlog" resultMap="blogResult">

  SELECT * FROM BLOG WHERE ID = #{id}</select>

<select id="selectAuthor" resultType="Author">

  SELECT * FROM AUTHOR WHERE ID = #{id}</select>

会导致N+1问题,但是mybatis提供了延时加载,如果你加载记录列表之后立刻就遍历列表以获取嵌套的数据,会触发所有的延迟加载查询,性能可能会变得很糟糕。

 

另一种解决方式:关联的嵌套结果映射

将博客表和作者表连接在一起,而不是执行一个独立的查询语句

<select id="selectBlog" resultMap="blogResult">

  select

    B.id            as blog_id,

    B.title         as blog_title,

    B.author_id     as blog_author_id,

    A.id            as author_id,

    A.username      as author_username,

    A.password      as author_password,

    A.email         as author_email,

    A.bio           as author_bio

  from Blog B left outer join Author A on B.author_id = A.id

  where B.id = #{id}</select>

<resultMap id="blogResult" type="Blog">

  <id property="id" column="blog_id" />

  <result property="title" column="blog_title"/>

  <association property="author" javaType="Author">

    <id property="id" column="author_id"/>

    <result property="username" column="author_username"/>

    <result property="password" column="author_password"/>

    <result property="email" column="author_email"/>

    <result property="bio" column="author_bio"/>

  </association></resultMap>

从版本 3.2.3 开始,MyBatis 提供了另一种解决 N+1 查询问题的方法。

关联的多结果集(ResultSet)

存储过程执行下面的查询并返回两个结果集。第一个结果集会返回博客(Blog)的结果,第二个则返回作者(Author)的结果。

在映射语句中,必须通过 resultSets 属性为每个结果集指定一个名字,多个名字使用逗号隔开。

<select id="selectBlog" resultSets="blogs,authors" resultMap="blogResult" statementType="CALLABLE">

  {call getBlogsAndAuthors(#{id,jdbcType=INTEGER,mode=IN})}</select>

<resultMap id="blogResult" type="Blog">

  <id property="id" column="id" />

  <result property="title" column="title"/>

  <association property="author" javaType="Author" resultSet="authors" column="author_id" foreignColumn="id">

    <id property="id" column="id"/>

    <result property="username" column="username"/>

    <result property="password" column="password"/>

    <result property="email" column="email"/>

    <result property="bio" column="bio"/>

  </association></resultMap>

集合的嵌套 Select 查询

嵌套 Select 查询来为博客加载文章。

<resultMap id="blogResult" type="Blog">

  <collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/></resultMap>

<select id="selectBlog" resultMap="blogResult">

  SELECT * FROM BLOG WHERE ID = #{id}</select>

<select id="selectPostsForBlog" resultType="Post">

  SELECT * FROM POST WHERE BLOG_ID = #{id}</select>

“ofType” 属性。这个属性非常重要,它用来将 JavaBean(或字段)属性的类型和集合存储的类型区分开来。

<select id="selectBlog" resultMap="blogResult">

  select

  B.id as blog_id,

  B.title as blog_title,

  B.author_id as blog_author_id,

  P.id as post_id,

  P.subject as post_subject,

  P.body as post_body,

  from Blog B

  left outer join Post P on B.id = P.blog_id

  where B.id = #{id}</select>

<resultMap id="blogResult" type="Blog">

  <id property="id" column="blog_id" />

  <result property="title" column="blog_title"/>

  <collection property="posts" ofType="Post">

    <id property="id" column="post_id"/>

    <result property="subject" column="post_subject"/>

    <result property="body" column="post_body"/>

  </collection></resultMap>

自动映射

单的场景,自动映射查询结果。复杂的场景,你需要构建一个结果映射。 可以混合使用这两种策略。

有三种自动映射等级:

  • NONE - 禁用自动映射。仅对手动映射的属性进行映射。
  • PARTIAL - 对除在内部定义了嵌套结果映射(也就是连接的属性)以外的属性进行映射
  • FULL - 自动映射所有属性

默认值是 PARTIAL,这是有原因的。当对连接查询的结果使用 FULL 时,连接查询会在同一行中获取多个不同实体的数据,因此可能导致非预期的映射。

 

通过在结果映射上设置 autoMapping 属性来为指定的结果映射设置启用/禁用自动映射。

 

缓存

MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制

默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:

<cache/>

这个简单语句的效果如下:

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

使用自定义缓存

除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。

<cache type="com.domain.something.MyCustomCache"/>

type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。

 

动态 SQL

if

使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:

<select id="findActiveBlogWithTitleLike"

     resultType="Blog">

  SELECT * FROM BLOG

  WHERE state = ‘ACTIVE’

  <if test="title != null">

    AND title like #{title}

  </if></select>

choose、when、otherwise

不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素

<select id="findActiveBlogLike"

     resultType="Blog">

  SELECT * FROM BLOG WHERE state = ‘ACTIVE’

  <choose>

    <when test="title != null">

      AND title like #{title}

    </when>

    <when test="author != null and author.name != null">

      AND author_name like #{author.name}

    </when>

    <otherwise>

      AND featured = 1

    </otherwise>

  </choose></select>

trim、where、set

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除

<select id="findActiveBlogLike"

     resultType="Blog">

  SELECT * FROM BLOG

  <where>

    <if test="state != null">

         state = #{state}

    </if>

    <if test="title != null">

        AND title like #{title}

    </if>

    <if test="author != null and author.name != null">

        AND author_name like #{author.name}

    </if>

  </where></select>

可以通过自定义 trim 元素来定制 where 元素的功能

 

用于动态更新语句的类似解决方案叫做 setset 元素可以用于动态包含需要更新的列,忽略其它不更新的列。会删掉额外的逗号

<update id="updateAuthorIfNecessary">

  update Author

    <set>

      <if test="username != null">username=#{username},</if>

      <if test="password != null">password=#{password},</if>

      <if test="email != null">email=#{email},</if>

      <if test="bio != null">bio=#{bio}</if>

    </set>

  where id=#{id}</update>

foreach

<select id="selectPostIn" resultType="domain.blog.Post">

  SELECT *

  FROM POST P

  WHERE ID in

  <foreach item="item" index="index" collection="list"

      open="(" separator="," close=")">

        #{item}

  </foreach></select>

将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值

 

 

多数据库支持

配置了 databaseIdProvider

<insert id="insert">

  <selectKey keyProperty="id" resultType="int" order="BEFORE">

    <if test="_databaseId == 'oracle'">

      select seq_users.nextval from dual

    </if>

    <if test="_databaseId == 'db2'">

      select nextval for seq_users from sysibm.sysdummy1"

    </if>

  </selectKey>

  insert into users values (#{id}, #{name})</insert>

 

 

 

 

 

org.apache.ibatis.binding.BindingException: Type interface com.wang.mapper.UserMapper is not known to the MapperRegistry.

 

没有注册mapper

解决:

<mappers>
    <mapper resource="com/wang/mapper/UserMapper.xml"/>

<mapper class="com.wang.mapper.UserMapper"/>
</mappers>

 

使用resource最好,写在resource或者和mapper在同一个包下,都可以

使用class,必须mapper和xml同名,且必须在同一个包。

使用package name=”com.wang.mapper”  

报错:java.lang.ExceptionInInitializerError

The error may exist in com/wang/mapper/UserMapper.xml

 

Maven约定大于配置,写的配置文件,可能无法导出或生效

在pom中加上配置

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

 

增删改操作,需要提交事务 commit

 

 

 

 

 

如果字段过多,可以使用Map

 

Map传递参数,直接在sql中取出key

parameterType="map"

 

对象传递参数,要在sql中取对象的属性

parameterType="Object"

 

只有一个基本参数类型参数,直接在sql取出

<select id="selectById" resultType="com.wang.entity.User" parameterType="int">
    select * from mybatis.user where id=#{id}
</select>

可以不写paramterType=”int”

 

多个参数使用Map或者注解

 

 

 

 

 

 

 

 

 

对象:使用

一对多处理:::多个学生一个老师   关联查询

第一种:子查询

<!--查询学生,并得到关联的老师 -->
<select id="getStudent" resultMap="studentResult">
    select * from mybatis.student;
</select>
<resultMap id="studentResult" type="Student">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <!--复杂属性 对象:使用association 集合:使用collection-->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>

</resultMap>
<select id="getTeacher" resultType="teacher">
    select * from mybatis.teacher where id=#{id}
</select>

第二种:按照结果嵌套处理

 

<select id="getstudent2" resultMap="studentMap2">
    select s.id sid,s.name sname,t.name tname
    from student  s,teacher t  where s.tid=t.id;
</select>
<resultMap id="studentMap2" type="Student">
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>

 

一对多处理:::一个老师多个学生

<select id="getTeacher" resultType="teacher">
    select * from mybatis.teacher
</select>

    <!--结果嵌套查询-->
<select id="getTeacherAndStu" resultMap="teacherMap">
    select s.id sid,s.name sname,t.name tname,t.id tid
    from student  s,teacher t
    where s.tid=t.id and t.id=#{tid}
</select>
    <resultMap id="teacherMap" type="Teacher">
        <id property="id" column="tid"/>
        <result property="name" column="tname"/>
        <!--javaType:指定属性的类型
           集合中的泛型信息,使用ofType属性
        -->
        <collection property="students" ofType="Student">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
            <result property="tid" column="tid"/>
        </collection>
    </resultMap>
    <!--子查询-->
    <select id="getTeacherAndStu1" resultMap="teacherMap2">
        select * from mybatis.teacher where id=#{tid}
    </select>
    <resultMap id="teacherMap2" type="Teacher">
        <id property="id" column="id"/>
        <collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentById"/>
    </resultMap>
    <select id="getStudentById" resultType="Student">
        select * from mybatis.student where tid=#{tid}
    </select>

 

日志

控制台打印出sql

日志工厂:默认slf4j|log4j

标准日志工厂实现:

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

 

Log4j实现:客人已通过配置文件灵活配置

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

 

缓存

读写分离

主从复制

默认开启一级缓存  基于sqlsession

二级缓存需要手动开启  基于namespace  实现Cache接口

 

缓存失效:

1、增删改都会刷新缓存

2、查询不同的数据

3、查询不同的mapper.xml

4、清理缓存  sqlsession.clearCache();

 

开启二级缓存(全局缓存)

<setting name=”cacheEnabled” value=”true”></setting>

Mapper.xml 中<cache/>

 

缓存原理:

顺序:用户进来==》二级缓存==》一级缓存==》数据库

 

 

 

 

LRU:最近最少使用原则

FIFO:先进先出原则

不同的mapper查询的数据会放在自己对应的map中

Mybatis详细执行流程

Resource===》加载全局配置文件==》实例化sqlsessionbuilder构造器===》build方法 解析配置文件XMLconfiguration对象的parse()方法来具体解析mybatis配置文件,返回一个 --configuration对象作为参数又返回给一个build方法,返回值是一个sqlsessionfactory类型的 DefaultSql'SessionFactory====》sqlsessionfactory是一个接口 实现类DefaultSql'SessionFactory实例化===》opensession方法返回sqlsession(接口),DefaultSql'SessionFactory类final xecutor执行器用SimpleExecutor来执行,实现类、事务工厂等

Sqlswssion接口中由getMapper(类.class)方法,通过反射获得mapper的方法,根据mapperxml映射执行具体增删改查sql语句

jdk动态代理生成mapper接口的代理对象,通过mapperRegistry的对象,

return mapperProxyFactory.newInstance(sqlSession);  执行代理对象 mapperProxy的invoke方法

 

return new DefaultSqlSession(configuration, executor, autoCommit);

 

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  Transaction tx = null;
  try {
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    final Executor executor = configuration.newExecutor(tx, execType);
    return new DefaultSqlSession(configuration, executor, autoCommit);

 

 

注解:多个简单类型参数 使用@Param注解基本类型和string类型加上,引用类型不需要

@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中   

@Select(“selext * from user where id=#{uid}”)

List<user> getUser(@Param(“uid”) int id ,@param(“namae”)string name)

@param( "uid ")是数据库中的字段名,id是类中的属性名

使用注解,可以不用paramterType

 

public List<UserExtension> selectUser( @Param("user") UserExtension user);

<select id=" selectUser" resultMap="BaseResultMap">

   select  *  from user_user_t   where user_name = #{user.userName,jdbcType=VARCHAR} and user_area=#{user.userArea,jdbcType=VARCHAR}

</select>

 

不使用@Param注解,同样也可以传递多个参数(尤其是多个 javaBean)

1、DAO层的函数方法

Public User selectUser(String name,String area);

对应的Mapper.xml

<select id="selectUser" resultMap="BaseResultMap">

    select  *  from user_user_t   where user_name = #{0} and user_area=#{1}

</select>

其中,#{0}代表接收的是dao层中的第一个参数,#{1}代表dao层中第二参数

2、Map传参

Public User selectUser(Map paramMap);

Service层调用

Private User xxxSelectUser(){Map paramMap=new hashMap();paramMap.put(“userName”,”对应具体的参数值”);paramMap.put(“userArea”,”对应具体的参数值”);User user=xxx. selectUser(paramMap);}

 

<select id=" selectUser" resultMap="BaseResultMap"> select * from user_user_t where user_name = #{userName,jdbcType=VARCHAR} and user_area=#{userArea,jdbcType=VARCHAR} </select>

3、Dao层的函数方法

Public User selectUser(@param(“userName”)String name,@param(“userArea”)String area);

<select id=" selectUser" resultMap="BaseResultMap"> select * from user_user_t where user_name = #{userName,jdbcType=VARCHAR} and user_area=#{userArea,jdbcType=VARCHAR} </select>

使用@Param时注意的问题

  • 当用@Param注解来声明参数时,使用#{}或者${}都可以
  • 当不使用@Param注解时,必须使用#{},否则会报错

@Mapper

public interface UserMapper {

    List<User> getAllUsers(@Param("order_by")String order_by);

}

对应的 XML 定义如下:

<select id="getAllUsers" resultType="org.javaboy.helloboot.bean.User">

    select * from user

    <if test="order_by!=null and order_by!=''">

        order by ${order_by} desc

    </if>

</select>

在动态 SQL 中使用了参数作为变量,需要 @Param 注解

在动态 SQL 中用到了 参数作为判断条件,要加 @Param 注解的

 

 

多对多:用户user==角色role  加中间表user_role

<resultMap id="userMap" type="com.example.domain.User">

        <id property="id" column="id"/>

        <result property="username" column="username"/>

        <result property="birthday" column="birthday"/>

        <result property="sex" column="sex"/>

        <result property="address" column="address"/>

        <!--<collection property="roles" ofType="com.example.domain.Role" resultMap="roleMap"/>-->

        <collection property="roles" ofType="com.example.domain.Role">
       <id property="roleId" column="rid"/>

            <result property="roleName" column="ROLE_NAME"/>

            <result property="roleDesc" column="ROLE_DESC"/>

        </collection>

    </resultMap>

 

<select id="findAll" resultMap="userMap">

       SELECT u.*,r.ID as rid,r.ROLE_DESC,r.ROLE_NAME FROM user u

         LEFT OUTER JOIN user_role ur on u.id = ur.UID

          LEFT OUTER JOIN role r on ur.RID = r.ID

</select>

collection也可以分开写引用结果映射

 <resultMap id="userMap" type="com.example.domain.User">

        <id property="id" column="id"/>

        <result property="username" column="username"/>

        <result property="birthday" column="birthday"/>

        <result property="sex" column="sex"/>

        <result property="address" column="address"/>

        <collection property="roles" ofType="com.example.domain.Role" resultMap="roleMap"/>

    </resultMap>

    <resultMap id="roleMap" type="com.example.domain.Role">

        <id property="roleId" column="rid"/>

        <result property="roleName" column="ROLE_NAME"/>

        <result property="roleDesc" column="ROLE_DESC"/>

    </resultMap>

 

 

 

 

<mapper namespace="com.zking.mapper.IPerson">

<!-- resultMap:映射实体类和字段之间的一一对应的关系 -->

<resultMap id="selectPersonList" type="person">

<id column="pid" property="pid"></id>

<result column="pname" property="pname"></result>

<!-- 多对多关联映射:collection -->

<collection property="cityList" ofType="city">

<id column="cid" property="cid"></id>

<result column="cname" property="cname"></result>

</collection>

</resultMap>

<select id="selectPerson" resultMap="selectPersonList">

SELECT p.*,ci.*,ca.`cno` FROM person p, city ci, card ca

WHERE p.`pid`=ca.`pid` AND ci.`cid`=ca.`cid`

</select>

 

mybatis中如果返回对象集合的话,会把对象中的所有字段都返回,如果表中字段很多而我只需要部分字段,有几种解决方案

1、重新定义类,里面存放要返回的字段属性

2、将结果定义为List<Map<String, Object>>类型

List<Map<String, Object>> selectPartBook();

<resultMap id="PartBookMap" type="java.util.HashMap">

 

 

 

MyBatis 四大对象(

Executor、拦截内部执行器:

StatementHandler、拦截SQl语法构建

ParameterHandler、拦截参数

ResultSetHandler) 拦截结果集处理

 

Mybatis分页方式 四种

1、数组分页  全部取出 list截取

2、Sql分页  limit 第几页  记录数

3、拦截器分页 implements Interceptor

4、Rowbounds分页  数据量小使用,大使用拦截器

 

Rowbounds,并非是一次性查询出所有数据,因为 MyBatis 是对 jdbc 的封装,在 jdbc 驱动中有一个 Fetch Size 的配置,它规定了每次最多从数据库查询多少条数据,假如你要查询更多数据,它会在你执行 next()的时候,去查询更多的数据。

 

Mybatis逻辑分页核物理分页区别:

物理分页,数据库,效率高,例如mysql的limit  个数据库实现不同 oracle是rownum

逻辑分页:利用游标分页,效率低;

 

MyBatis使用RowBounds实现的分页是逻辑分页;全查,再offset和limit截断记录返回。

插件分页:Mybatis-PageHelper

 

Mybatis有三种基本的Executor执行器:
SimpleExecutor、ReuseExecutor、BatchExecutor



这篇关于mybatis的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程