MyBatis框架问题总结

一,概念

MyBatis是一个对象关系映射框架,简称ORM框架。RM框架提供了持久化类与表的映射关系,在运行时参照映射文件的信息,把对象持久化到数据库中。
目前常见的ORM框架有mybatis和hibernate,两者的区别主要是:
1,sql方面
Hibernate 自动生成sql,部分语句较为繁琐,会多消耗性能;适用于传统管理类项目。
Mybatis 手动编写sql,可避免不必要的查询,自主优化,提高系统性能;适用于注重性能的互联网项目。

2,开发方面
hibernate开发中,完全面向对象封装,sql语句已经被封装,直接可以使用,加快系统开发,但开发者不容易理解底层实现。
Mybatis开发中,自行管理映射关系, 属于半自动化,sql需要手工完成,稍微繁琐,但容易让开发者理解底层实现。
3,缓存方面
Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表-对象映射中配置是哪种缓存。

MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,针对不同的表可自定义不同缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。
4,数据库的移植性
MyBatis不能支持数据库无关性,即数据库发生变更,要写多套代码进行支持,移植性不好。
Hibernate具有良好的数据库无关性,即数据库发生变化的话,代码无需再次编写。以后,mysql数据迁移到oracle,只需要改方言配置

二,MyBatis的sql参数绑定

1,序号(原生)参数绑定
参数顺序对应sql语句参数顺序

//id对应arg0,pwd对应arg1
 public User selectUserByIdAndPwd(Integer id , String pwd);
SELECT * FROM t_users
    WHERE id = #{arg0} AND password = #{arg1} 

2,注解(原生)参数绑定
注解别名对应sql参数名

 public User selectUserByIdAndPwd(@Param("id")Integer aaa, 
 @Param("pwd")String bbb);
 SELECT * FROM t_users
    WHERE id = #{id} AND password = #{pwd}

3,Map参数绑定
键值对应sql参数名

Map values = new HashMap(); //测试类创建Map
values.put("myId",1); //自定义key,绑定参数
values.put("myPwd","123456");
User user = userDao.selectUserByIdAndPwd_map(values);
 SELECT * FROM t_users 
  	WHERE id = #{myId} AND password = #{myPwd}

4,对象参数绑定
对象属性名对应sql参数名

public User selectUserByUserInfo(User user);
SELECT * FROM t_users
    WHERE id = #{id} AND password = #{password}

5,$符号参数绑定
${attribute} 属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态生降序问题

User user = new User(....);
List<User> ulist1 = userDAO.selectAllUsers1(user); //调用时传入user对象
List<User> ulist2 = userDao.selectAllUsers2("desc"); //调用时传入asc | desc

<select id="selectAllUsers1" resultType="user">
	SELECT * FROM t_users 
    WHERE name = '${name}' or id = ${id} <!-- 拼接name和id,如果是字符类型需要用单引号:'${name}' -->
</select>
<select id="selectAllUsers2" resultType="user">
	SELECT * FROM t_users 
  	ORDER BY id ${rule} <!-- 拼接 asc | desc -->
</select>

6,模糊查询
keyword是传入的关键字参数

SELECT * FROM t_users 
  		WHERE name LIKE concat('%',#{keyword},'%')

三,主键回传两种方式

1, 通过last_insert_id()查询主键

<insert id="insertProduct" parameterType="product">
		 <!-- order=“after”代表插入之后 -->
        <selectKey keyProperty="id" resultType="int" order="AFTER">
         <!-- 适用于整数类型自增主键 -->
            SELECT LAST_INSERT_ID()
        </selectKey>
        INSERT INTO t_product(id,name) VALUES(#{id},#{name})
</insert>

2,通过uuid()查询主键

 <insert id="insertOrder" parameterType="order">
 		<!-- order=“before”插入之前 -->
        <selectKey keyProperty="id" resultType="String" order="BEFORE">
        <!-- 适用于字符类型主键 -->
            SELECT REPLACE(UUID(),'-','') 
        </selectKey>
        INSERT INTO t_order(id,name) VALUES(#{id},#{name})
    </insert>

注意:两种主键的回传方式都是回传到了传递的参数——类的对象的主键属性中,可通过主键get方法得到。

三,ORM映射

1,列的别名
在SQL中使用 as 为查询字段添加列别名,以匹配属性名

SELECT mgr_id AS id , mgr_name AS username , mgr_pwd AS password
        FROM t_managers
        WHERE mgr_id = #{id} AND mgr_pwd = #{pwd}

2,结果映射(ResultMap - 查询结果的封装规则)

<!--定义resultMap标签-->
    <resultMap id="managerResultMap" type="com.qf.mybatis.part2.orm.Manager">
      	<!--关联主键与列名-->
        <id property="id" column="mgr_id" />
      	<!--关联属性与列名-->
        <result property="username" column="mgr_name" />
        <result property="password" column="mgr_pwd" />
    </resultMap>
     <!--使用resultMap作为ORM映射依据-->
    <select id="selectAllManagers" resultMap="managerResultMap">
        SELECT mgr_id , mgr_name , mgr_pwd
        FROM t_managers
    </select>

3,驼峰式映射
mybatis配置文件中添加,注意:只适用于驼峰式映射

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

四,MyBatis处理关联关系-多表连接

1,一对一关系
典型一对一关系:一个订单详情对应一种商品
使用association 标签,javaType属性表示关联的类

<!-- 结果映射(查询结果的封装规则) -->
    <resultMap id="passengerResultMap" type="com.qf.mybatis.part2.one2one.Passenger">
        <id property="id" column="id"/>
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <result property="birthday" column="birthday" />
      	<!-- 关系表中数据的封装规则 -->	 <!-- 指定关系表的实体类型 -->
        <association property="passport" javaType="com.qf.mybatis.part2.one2one.Passport">
            <id property="id" column="passport_id" />
            <result property="nationality" column="nationality" />
            <result property="expire" column="expire" />
          	<result property="passenger_id" column="passenger_id" />
        </association>
    </resultMap>
  	<!-- 多表连接查询 -->					  	<!-- 结果映射(查询结果的封装规则)-->
    <select id="selectPassengerById" resultMap="passengerResultMap">
        <!-- 别名(避免与p1.id冲突) -->
        SELECT p1.id , p1.name , p1.sex , p1.birthday , p2.id as passport_id , p2.nationality , p2.expire 			, p2.passenger_id
        FROM t_passengers p1 LEFT JOIN t_passports p2
        ON p1.id = p2.passenger_id
        WHERE p1.id = #{id}
    </select>

2,一对多关系
典型一对多关系: 一张订单对应多个订单详情
使用collection标签,ofType属性表示关联的类

<!-- 封装规则 -->
    <resultMap id="departmentResultMap" type="com.qf.mybatis.part2.one2many.Department">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="location" column="location" />
      	<!-- 关系表中数据的封装规则 -->		<!-- 指定关系表的实体类型 -->
        <collection property="emps" ofType="com.qf.mybatis.part2.one2many.Employee">
            <id property="id" column="emp_id" />
            <result property="name" column="emp_name" />
            <result property="salary" column="salary" />
            <result property="dept_id" column="dept_id" />
        </collection>
    </resultMap>
  	<!-- 多表连接查询 -->			      <!-- 封装规则 -->
    <select id="selectDepartmentById" resultMap="departmentResultMap" >
      	<!-- 别名(避免与d.id、d.name冲突)-->
        SELECT d.id , d.name , d.location , e.id AS emp_id , e.name emp_name , e.salary , e.dept_id
        FROM t_departments d LEFT JOIN t_employees e
        ON d.id = e.dept_id
        WHERE d.id = #{id}
    </select>

3,多对多关系
典型多对多关系:一个学生有多种课程,一种课程有多个学生
建立第三张表存放两表外键,两个外键为联合主键

<!-- 映射查询只封装两表中的信息,可忽略关系表内容 -->
    <resultMap id="allMap" type="com.qf.mybatis.part2.many2many.Student">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <collection property="subjects" ofType="com.qf.mybatis.part2.many2many.Subject">
            <id property="id" column="sid" />
            <result property="name" column="sname" />
            <result property="grade" column="grade" />
        </collection>
    </resultMap>
  	<!-- 三表连接查询 -->
    <select id="selectAllStudents" resultMap="allMap">
        SELECT s1.* , ss.* , s2.id as sid , s2.name as sname , s2.grade
        FROM t_students s1 LEFT JOIN t_stu_sub ss
        ON s1.id = ss.student_id <!-- 通过t_stu_sub表建立二者之间的关系 -->
        LEFT JOIN t_subjects s2
        ON ss.subject_id = s2.id
    </select>

五,动态sql

1,include引用sql片段

<sql id="BOOKS_FIELD"> <!-- 定义SQL片段 -->
        SELECT id,name,author,publish,sort
</sql>
<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
    <!-- 通过ID引用SQL片段 -->
	<include refid="BOOKS_FIELD" /> FROM t_books
</select>

2,where 和 if

<select id="selectBookByCondition" resultType="com.qf.mybatis.part2.dynamic.Book">
    SELECT id , name , author , publish , sort
    FROM t_books
    <where> <!-- WHERE,会自动忽略前后缀(如:and | or) -->
        <if test="id != null"> id = #{id}</if>
        <if test="name != null">and name = #{name}</if>
        <if test="author != null">and author = #{author}</if>
        <if test="publish != null"> and publish = #{publish}</if>
        <if test="sort != null">and sort = #{sort}</if>
    </where>
</select>

3,set

<update id="updateBookByCondition">
    UPDATE t_books
    <set><!-- where子句中满足条件的if,会自动忽略后缀(如:,) -->
        <if test="name != null">name = #{name} ,</if>
        <if test="author != null">author = #{author} ,</if>
		<if test="publish != null">publish = #{publish} ,</if>
		<if test="sort != null">sort = #{sort} ,</if>
    </set>
    WHERE id = #{id}
</update>

4,trim
< trim prefix=“” suffix=“” prefixOverrides=“” suffixOverrides=“” >代替< where > 、< set >

<select id="selectBookByCondition" resultType="com.qf.mybatis.day2.dynamic.Book">
SELECT id,name,author,publish,sort
    FROM t_books
    <trim prefix="WHERE" prefixOverrides="AND|OR"> <!-- 增加WHERE前缀,自动忽略前缀 -->
        <if test="id != null"> and id = #{id}</if>
        <if test="name != null">and name = #{name}</if>
        <if test="author != null">and author = #{author}</if>
        <if test="publish != null">and publish = #{publish}</if>
        <if test="sort != null">and sort = #{sort}</if>
	</trim></select>
<update id="updateBookByCondition">
		UPDATE t_books
		<trim prefix="SET" suffixOverrides=","> <!-- 增加SET前缀,自动忽略后缀 -->
				<if test="name != null">name = #{name} ,</if>
				<if test="author != null">author = #{author} ,</if>
				<if test="publish != null">publish = #{publish} </if>
				<if test="sort != null">sort = #{sort}</if>
    </trim>
		WHERE id = #{id}
</update>

5,foreach

<delete id="deleteBookByIds">
		DELETE FROM t_books
		WHERE id IN
		<foreach collection="list" open="(" separator="," close=")"  item="id" index="i">
				#{id}
		</foreach>
</delete>
 <insert id="insertOrdersDetail">
        insert into orders_detail (pid,oid,num,money) values
        <foreach collection="ordersDetails" item="ordersDetail" separator=",">
        (#{ordersDetail.pId},#{ordersDetail.oId},#{ordersDetail.num},#{ordersDetail.money})
        </foreach>
    </insert>

六,缓存

概念

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。
无缓存:用户在访问相同数据时,需要发起多次对数据库的直接访问,导致产生大量IO、读写硬盘的操作,效率低下
有缓存:首次访问时,查询数据库,将数据存储到缓存中;再次访问时,直接访问缓存,减少IO、硬盘读写次数、提高效率

1 ,一级缓存
SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。
注意:无需任何配置,默认开启一级缓存。
2 ,二级缓存
SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。
注意:在sqlSession.commit()或者sqlSession.close()之后生效。
3, 指定Mapper缓存
在指定表对应的mapper文件的sql语句前添加

 <cache />

4,全局缓存
mabatis配置文件中

<configuration>
	<properties .../>
  	<!-- 注意书写位置 -->
    <settings>
     	 <!--打印日志信息-->
     	<setting name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"/>
        <!--打印sql-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
         <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
  	<typeAliases></typeAliases>
</configuration>

七,Mybatis延迟加载的实现方式

1、概念:

MyBatis中的延迟加载,也叫懒加载,是指在进行表的关联查询时,按照设置的延迟规则推迟对关联对象的select查询。例如在进行一对多查询的时候,只查询出一方,当程序中需要多方的数据时,mybatis再发出sql语句进行查询,这样子延迟加载就可以的减少数据库压力。MyBatis 的延迟加载只是对关联对象的查询有迟延设置,对于主加载对象都是直接执行查询语句的。

2、加载时机:

直接加载:执行完对主加载对象的 select 语句,马上执行对关联对象的 select 查询。
侵入式延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。但当要访问主加载对象的详情属性时,就会马上执行关联对象的select查询。
深度延迟: 执行对主加载对象的查询时,不会执行对关联对象的查询。访问主加载对象的详情时也不会执行关联对象的select查询。只有当真正访问关联对象的详情时,才会执行对关联对象的 select 查询。

注意:延迟加载的应用要求:关联对象的查询与主加载对象的查询必须是分别进行的select语句,不能是使用多表连接所进行的select查询。因为,多表连接查询,实质是对一张表的查询,对由多个表连接后形成的一张表的查询。会一次性将多张表的所有信息查询出来。
侵入式延迟加载:
不调用主加载对象时只有一条SQL
调用主加载对象的信息时会产生两条SQL

<!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载   默认为false(深度加载)
      侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
      深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->
    <setting name="aggressiveLazyLoading" value="true"/>

深入式延迟加载:
调用主加载对象时不会执行第二条加载SQL
调用关联对象详细信息时会执行第二次查询

<!--开启延迟加载-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--配置侵入式延迟加载   默认为false(深度加载)
      侵入式:默认只会执行主加载SQL,那么当访问主加载对象的详细信息时才会执行关联对象的SQL查询
      深度延迟:默认只执行主加载SQL,那么当调用到主加载对象中关联对象的信息时才会执行关联对象的SQL查询
    -->
    <setting name="aggressiveLazyLoading" value="false"/>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值