Mybatis使用各种配置的缘由

Mybatis一些问题和优化:

问题:

  • 进行模糊查询的时候,xml中写"%"#{}"%" 会导致sql注入问题

将%dirname%写死成字符串blurname,直接传入blurname,使用#{value}获得

  • 数据库中的字段值和实体类的属性不对应

原因执行sql操作,将值传给对应实体类时,会查找属性的set方法,由于数据库的属性名和实体类不对应,所以导致set失败;so,会导致取不到值的情况

在sql语句中给数据库的中的属性名起别名为实体类中的属性名

eg:select id , name , pwd as password from user where id = #{id}

pwd为数据库中的名、password为实体类的名

优化:

  • mybatis-config.xml配置中数据库参数的冗长问题

properties优化

为了简化配置文件的编写,我们可以引入db.properties配置文件,
在configuration下的第一行加入
这样只需要在对应的value中写${}参数即可

  • Mapper.xml中对象限定名冗长

在标签中添加typeAliases 可以对完整的限定名起别名,简化书写

  • 进行数据库数据回传时,若属性名过多,导致取值很麻烦

1、可以使用map自动映射参数和其值

2、使用手动设定map映射关系

<!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
	<!-- id:设置ResultMap的id 在select中ResultMap对应这个值-->
	<resultMap type="order" id="orderResultMap">
		<!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
		<!-- property:主键在pojo中的属性名 -->
		<!-- column:主键在数据库中的列名 -->
		<id property="id" column="id" />
 
		<!-- 定义普通属性 -->
		<result property="userId" column="user_id" />
		<result property="number" column="number" />
		<result property="createtime" column="createtime" />
		<result property="note" column="note" />
	</resultMap>
  • 提升sql排错效率

日志工厂 在mybatis-config.xml下configuration标签下

 <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
  • 分页机制

使得查询出的数据输出一定的范围数据,对数据库压力比较小

语法 select * from user limit startindex, pageSize;

select * from user limit 5, 10;//展示6-15的数据

select * from user limit 5, -1;//输出6-last的数据

使用map将startindex, pageSize参数put进去.

注解开发

注解分为:

  • @select()
  • @update()
  • @insert()
  • @delete()

我们只需要在对应的Mapper接口上注解对应的sql语句即可

@Select("select * from user")
public List<User> getAll();

在mybatis-config.xml文件中使用

<mappers>
    <mapper class="com.mapper.MapperUser"/>

多对一的处理

eg:多个学生对应一个老师

实体类:

public class Teacher{
    private String id;
    private String name;
}
public class Student{
    private String id;
    private String name;
    
	//多个学生可以是同一个老师,即多对一
    private Teacher teacher;
}
  • 按查询嵌套处理

方式: 查询所有的学生信息,使用resultMap进行接收,做一个结果集映射:StudentTeacher,类型为Student

association:一个复杂类型的关联;使用它来处理关联查询

<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>
  • 按结果嵌套查询处理
<!--
按查询结果嵌套处理
思路:
1. 直接查询出结果,进行结果集的映射
-->
<select id="getStudents2" resultMap="StudentTeacher2" >
select s.id sid, s.name sname , t.name tname
from student s,teacher t
where s.tid = t.id
</select>
<resultMap id="StudentTeacher2" 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>

小结:

  • 按查询嵌套查询处理:类似sql的子查询
  • 按结果嵌套查询处理:类似sql的联表查询

一对多的处理

实体类:

public class Student{
    private String id;
    private String name;
}
public class Teacher{
    private String id;
    private String name;
    //一个老师对应多个学生
    private List<Student> students;
}
  • 按结果嵌套查询处理
<mapper namespace="com.kuang.mapper.TeacherMapper">
<!--
思路:
1. 从学生表和老师表中查出学生id,学生姓名,老师姓名
2. 对查询出来的操作做结果集映射
1. 集合的话,使用collection!
JavaType和ofType都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型
ofType指定的是映射到list集合属性中pojo的类型。
-->
    <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>
</mapper>
  • 按查询嵌套处理
<select id="getTeacher2" resultMap="TeacherStudent2">
	select * from teacher where id = #{id}
</select>
<resultMap id="TeacherStudent2" 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>

**小结 **

  • 关联-association:是用于一对一和多对一
  • 集合-collection:用于一对多的关系
  • .JavaType和ofType都是用来指定对象类型的
    • JavaType是用来指定pojo中属性的类型—>对应pojo中的参数类型 如一对多中的List
    • ofType指定的是映射到list集合属性中pojo的类型

动态SQL

  • if
  • choose(when,otherwise)
  • trim(where,set)
  • foreach

if语句

**需求:**根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则 根据作者名来查询

<select id="queryByIf" parameterType="map" resultType="blog">
	select * from blog where
    <if test="title != null">
    	title=#{title}
    </if>
    <if test="author != null">
    	and author=#{author}
    </if>
</select>

存在问题:当title为null时, SQL语句为select * from user where and author=#{author},这是显然错误的.

So,修改上面的语句

<select id="queryByIf" parameterType="map" resultType="blog">
	select * from blog 
    <where>
        <if test="title != null">
    	title=#{title}
        </if>
        <if test="author != null">
            and author=#{author}
        </if>
    </where>
    
</select>

结果:这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返 回的内容是以AND 或OR 开头的,则它会剔除掉。

Set语句

<!--注意set是用的逗号隔开-->
<update id="updateBlog" parameterType="map">
	update blog
	<set>
		<if test="title != null">
			title = #{title},
		</if>
		<if test="author != null">
			author = #{author}
		</if>
	</set>
	where id = #{id};
</update>

choose语句

有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句

<select id="queryByChoose" parameterType="map" resultType="blog">
	select * from blog
    <where>
    	<choose>
        	<when test="title != null">
            	title = #{title},
            </when>
            <when test="author != null">
            	author = #{author}
            </when>
            <otherwise>
            	and view=#{view}
            </otherwise>
        </choose>
    </where>
</select>

注意:

1.test中的key不能使用特殊字符 - 或者 +
2.test中的判断仅对!= null有效,不能使用==对字符串判断

if与choose对比:

  • 使用if标签时,只要test中的表达式为 true,就会执行 if 标签中的条件,so,可以选择多个条件

  • choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql.

SQL片段

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽 取出来,然后使用时直接调用。

提取片段:

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

引用SQL片段:

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

</select>

注意:

  • 最好基于 单表来定义 sql 片段,提高片段的可重用性
  • 在 sql 片段中不要包括 where

foreach语句

需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息

<select id="queryBlogForeach" parameterType="map" resultType="blog">
	select * from blog
    <where>
    	<!--
        collection:指定输入对象中的集合属性
        item:每次遍历生成的对象
        open:开始遍历时的拼接字符串
        close:结束时拼接的字符串
        separator:遍历对象之间需要拼接的字符串
        select * from blog where 1=1 and (id=1 or id=2 or id=3)
        -->
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
        	id=#{id}
        </foreach>
    </where>
</select>

注意:这里的ids是一个集合list

HashMap map = new HashMap();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);

Mybatis缓存

  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二 级缓存

一级缓存

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中。
  • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

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

  • 一级缓存失效情况:没有使用到当前的一级缓存,效果就是,还需要再向数据库中发起一次查询请 求!
    • sqlSession不同
    • sqlSession相同,查询参数||条件不同
    • sqlSession相同,两次查询之间执行了增删改操作(理解为有事务提交)
    • sqlSession相同,手动清除一级缓存

一级缓存就是一个map

二级缓存

  • 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;

  • 工作机制

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一 级缓存中的数据被保存到二级缓存中;
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;
  • 开启全局缓存

1.在mybatis-config.xml中配置

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

2.去每个mapper.xml中配置使用二级缓存,这个配置非常简单;

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

结论:

  • 只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
  • 查出的数据都会被默认先放在一级缓存中
  • 只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中

缓存原理

在这里插入图片描述


完结撒花!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值