首页 > 其他 > 详细

MyBatis小结

时间:2019-11-03 18:51:59      阅读:84      评论:0      收藏:0      [点我收藏+]

MyBatis

MyBatis官方文档:https://mybatis.org/mybatis-3/zh/index.html

Maven依赖

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>x.x.x</version>
</dependency>

XML配置

配置项(顺序固定)

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

properties(属性)

既可引入外部配置文件也可内部配置

config.properties:
    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
    username=root
    password=123456

<properties resource="org/mybatis/example/config.properties">
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
</properties>
...
<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

加载顺序,后加载的覆盖先加载的

  1. properties 元素体内
  2. properties 引入
  3. 方法参数传递

类型别名(typeAliases)

  1. 为每个类设置别名

     <typeAliases>
         <typeAlias type="com.pojo.User" alias="User"/>
     </typeAliases>
  2. 指定包名,没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名

     <typeAliases>
         <package name="com.pojo"/>
     </typeAliases>
  3. 内置别名

    • 基本数据类型前面加上下划线如:_int 对应 int
    • 引用数据类型小写如:int 对应 Integer

mappers(映射器)

  1. 引入资源方式

     <!-- 使用相对于类路径的资源引用 -->
     <mappers>
         <mapper resource="org/mybatis/builder/PostMapper.xml"/>
     </mappers>
    
     <!-- 使用完全限定资源定位符(URL) -->
     <mappers>
         <mapper url="file:///var/mappers/AuthorMapper.xml"/>
     </mappers>
    
     <!-- 
     使用映射器接口实现类的完全限定类名
     需要配置文件名称和接口名称一致,并且位于同一目录下
     -->
     <mappers>
         <mapper class="org.mybatis.builder.AuthorMapper"/>
     </mappers>
    
     <!-- 
     将包内的映射器接口实现全部注册为映射器
     但是需要配置文件名称和接口名称一致,并且位于同一目录下
     -->
     <mappers>
         <package name="org.mybatis.builder"/>
     </mappers>
  2. mapper文件

     <?xml version="1.0" encoding="UTF-8" ?>
     <!DOCTYPE mapper
             PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
     <mapper namespace="com..mapper.UserMapper">
    
     </mapper>

结果集映射

简单结果集映射

<resultMap id="UserMap" type="User">
    <!-- id为主键 -->
    <id column="id" property="id"/>
    <!-- column是数据库表的列名 , property是对应实体类的属性名 -->
    <result column="name" property="name"/>
    <result column="pwd" property="password"/>
</resultMap>

<select id="selectUserById" resultMap="UserMap">
    select id , name , pwd from user where id = #{id}
</select>

多对一

所有学生(Student)对应同一个老师(Teacher)

按结果嵌套处理(常用)

<select id="getStudents" resultMap="StudentTeacher" >
    select s.id sid, s.name sname , t.name tname
    from student s,teacher t
    where s.tid = t.id
</select>

<resultMap id="StudentTeacher" type="Student">
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <!--关联对象property 关联对象在Student实体类中的属性-->
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname"/>
    </association>
</resultMap>

按查询嵌套处理

<select id="getStudents" resultMap="StudentTeacher">
    select * from student
</select>
<resultMap id="StudentTeacher" type="Student">
    <!--association关联属性  property属性名 javaType属性类型 column在多的一方的表中的列名-->
    <association property="teacher"  column="tid" javaType="Teacher" select="getTeacher"/>
</resultMap>
<!--
这里传递过来的id,只有一个属性的时候,下面可以写任何值
association中column多参数配置:
    column="{key=value,key=value}"
    其实就是键值对的形式,key是传给下个sql的取值名称,value是片段一中sql查询的字段名。
-->
<select id="getTeacher" resultType="teacher">
    select * from teacher where id = #{id}
</select>

一对多

一个老师拥有多个学生

按结果嵌套处理

<select id="getTeacher" resultMap="TeacherStudent">
    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=#{id}
</select>

<resultMap id="TeacherStudent" type="Teacher">
    <result  property="name" column="tname"/>
    <collection property="students" ofType="Student">
        <result property="id" column="sid" />
        <result property="name" column="sname" />
        <result property="tid" column="tid" />
    </collection>
</resultMap>

按查询嵌套处理

<select id="getTeacher" resultMap="TeacherStudent">
    select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent" type="Teacher">
    <!--column是一对多的外键 , 写的是一的主键的列名-->
    <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
    select * from student where tid = #{id}
</select>

动态SQL

if

<select id="findActiveBlogWithTitleLike" resultType="Blog">
    SELECT * FROM BLOG
    WHERE state = ‘ACTIVE’
    <if test="title != null">
        AND title like #{title}
    </if>
</select>

choose (when, otherwise)

<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG WHERE state = ‘ACTIVE’
    <choose>
        <when test="title != null">
            AND title like #{title}
        </when>
        <otherwise>
            AND featured = 1
        </otherwise>
    </choose>
</select>

trim (where, set)

<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

<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>


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

trim定制成set

<trim prefix="SET" suffixOverrides=",">
...
</trim>

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>

注解开发

简单映射

//Results可以用id命名,这里命名为studentMap,在其他方法中可以直接调用
@Select("select * from student")
@Results(id = "studentMap",value = {
    @Result(id=true,column = "sid",property = "sid"),
    @Result(column = "sname",property = "sname"),
    @Result(column = "sex",property = "sex")
})
List<Student> findAll();

//直接调用已有的映射
@Select("select * from student where sid=#{id}")
@ResultMap("studentMap")
Student findById(Integer integer);

插入数据后返回id问题,两种方式

  1. options,添加表的列名sid,指定成员变量的名id,设置useGeneratedKeys = true

  2. selectkey,基本的思路和xml配置一样,代码编写如下:

     @Insert("insert into student(sname,sex,birthday,cno) values(#{name},#{sex})")
     //@Options(useGeneratedKeys = true,keyColumn = "sid",keyProperty = "id")
     @SelectKey(statement = "select last_insert_id()",keyProperty = "id", keyColumn = "sid",resultType = int.class,before = false)
     void addStudent(Student student);

多对一

@One 注解(一对一)代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。

@One 注解属性介绍:

  • select 指定用来多表查询的 sqlmapper
  • fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。。

使用格式:@Result(column=" ",property="",one=@One(select=""))

@Select("select * from account")
@Results(id="accountMap",
    value= {
        @Result(id=true,column="id",property="id"),
        @Result(column="uid",property="uid"),
        @Result(column="money",property="money"),
        @Result(column="uid",
            property="user",
            one=@One(select="com.dao.IUserDao.findById",
        fetchType=FetchType.LAZY)
        )
    })
List<Account> findAll();


@Select("select * from user where id = #{uid} ")
@ResultMap("userMap")
User findById(Integer userId);

一对多

@Many 注解(多对一)代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。

注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义;

使用格式:@Result(property="",column="",many=@Many(select=""))

@Select("select * from user")
@Results(id="userMap",
    value= {
        @Result(id=true,column="id",property="userId"),
        @Result(column="username",property="userName"),
        @Result(column="sex",property="userSex"),
        @Result(column="id",property="accounts",
            many=@Many(
                select="com.dao.IAccountDao.findByUid",
                fetchType=FetchType.LAZY
                )
            )
    })
List<User> findAll();

说明:
@Many:
    相当于<collection>的配置
    select 属性:代表将要执行的 sql 语句
    fetchType 属性:代表加载方式,一般如果要延迟加载都设置为 LAZY 的值


@Select("select * from account where uid = #{uid} ")
List<Account> findByUid(Integer userId);

二级缓存

@CacheNamespace(blocking=true)//mybatis 基于注解方式实现配置二级缓存
public interface IUserDao {}

动态SQL

script

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:

@Update({"<script>",
    "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}",
    "</script>"})
void updateAuthorValues(Author author);

bind

bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。比如:

<select id="selectBlogsLike" resultType="Blog">
    <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
    SELECT * FROM BLOG
    WHERE title LIKE #{pattern}
</select>

SQL片段

提取SQL片段

<sql id="if-title-author">
    <if test="title != null">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

引用SQL片段:

<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
        <include refid="if-title-author"></include>
        <!-- 在这里还可以引用其他的 sql 片段 -->
    </where>
</select>

缓存

一级缓存

SqlSession级别的缓存,也称为本地缓存,默认开启

一级缓存失效的的四种情况

  1. SqlSession不同
  2. SqlSession相同,查询条件不同
  3. SqlSession,两次查询之间执行了增删改操作
  4. SqlSession相同,手动清除一级缓存 clearCache()

二级缓存

使用步骤

  1. 在配置文件中开启全局缓存 【mybatis-config.xml】

     <setting name="cacheEnabled" value="true"/>
  2. 在需要的mapper.xml中配置使用二级缓存【xxxMapper.xml】

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

    配置二级缓存

     <cache
     eviction="FIFO"
     flushInterval="60000"
     size="512"
     readOnly="true"/>

    这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。

知识点

MyBatis的优点?为什么用MyBatis而不用JDBC

解除SQL和代码的耦合,提供xml标签,支持编写动态SQL,支持对象与数据库的字段关系映射

#{}和${}的区别

  1. #{}是预编译处理,${}是字符串替换
  2. 处理#{}时,会将SQL中的#{}替换为?号,调用PreparedStatement的set方法赋值,防止SQL注入
  3. 处理${}时,直接将${}替换成变量的值
  4. 排序时使用order by 动态参数时需要注意,用$而不是#

模糊查询like语句写法

  1. 在Java代码中添加通配符%
  2. 在sql中拼接
    1. 使用${...}like ‘%${value}%‘
    2. 使用#{...}"%"#{value}"%"

Mapper 接口的工作原理

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

如何获取自动生成的(主)键值?

如果采用主键自增长策略,自动生成的键值在insert执行完后被设置到传入的参数对象中,需要加上配置:

<insert id=”insertname” usegeneratedkeys=”true” keyproperty=”id”>

传递多个参数的方法

  1. 顺序传参

     public User selectUser(String name, int deptId);
     <select id="selectUser" resultMap="UserResultMap">
         select * from user where user_name = #{0} and dept_id = #{1}
     </select>
  2. @Param注解传参

     public User selectUser(@Param("userName") String name, @Param("deptId") int deptId);
     <select id="selectUser" resultMap="UserResultMap">
         select * from user where user_name = #{userName} and dept_id = #{deptId}
     </select>
  3. Map传参

     public User selectUser(Map<String, Object> params);
     <select id="selectUser" parameterType="map" resultMap="UserResultMap">
         select * from user where user_name = #{userName} and dept_id = #{deptId}
     </select>
  4. Java Bean传参

     public User selectUser(User user);
     <select id="selectUser" resultMap="UserResultMap">
         select * from user where user_name = #{UserName} and dept_id = #{deptId}
     </select>

结合Spring时一级缓存会失效

SqlSession交给了Spring管理,Spring在使用完后就会关闭

二级缓存的坑

二级缓存是mapper级别,当不同的mapper操作同一个数据时就会产生数据不一致

MyBatis原理

参考文章:MyBatis原理分析(通俗易懂)

MyBatis小结

原文:https://www.cnblogs.com/yxmhl/p/11787826.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!