MyBatis进阶

16 篇文章 0 订阅
7 篇文章 0 订阅

多对一(association关联)查询

mybatis会自动推断完全限定名,多数情况下都可以推断成功

按照结果嵌套处理

查询学生信息,同时返回对应的老师名字

Teacher

@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias("teacher")
public class Teacher {

  private int id;
  private String name;

}

Student里包含老师对象

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
  private int id;
  private String name;

  private Teacher teacher;

}

StudentMapper

List<Student> getStudent();

StudentMapper.xml

<select id="getStudent" resultMap="studentAndTeacherName">
  <!--多表联动查询-->
  select s.name sname, t.name tname
  from student s inner join teacher t on s.tid = t.id
</select>

<!--最终返回一个Student类型的对象-->
<resultMap id="studentAndTeacherName" type="student">
  <!--返回的表中字段sname赋值,给student对象的name属性-->
  <result property="name" column="sname"/>

  <!--复杂的属性,需要单独处理
association对象:返回对象中包含一个teacher类型的对象,类型(javaType)是teacher-->
  <association property="teacher" javaType="teacher">
    <!--把字段tname赋值给teacher对象的name属性-->
    <result property="name" column="tname"/>
  </association>
</resultMap>
List<Student> student = mapper.getStudent();

for (Student student1 : student) {
  System.out.println(student1.toString());
}

按照查询嵌套处理

<!--查出所有的学生信息:返回的字段有id,name,tid-->
  <select id="getStudent" resultMap="studentAndTeacherName">
  select * from student
  </select>

  <!--结果集映射,需要返回一个teacher类型-->
  <resultMap id="studentAndTeacherName" type="student">
  <result column="id" property="id"/>
  <!--这个teacher对象,由tid字段来赋值,这个tid从select中查询拿到,会自动和teacher的id进行匹配的-->
  <association property="teacher" javaType="teacher" column="tid" select="getTeacherById"/>
  </resultMap>

  <!--查询teacher对象,#{id}是从上面传过来的tid-->
  <select id="getTeacherById" resultType="teacher">
  select * from teacher where id = #{id};
</select>

一对多(collection集合)

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
  private int id;
  private String name;

  private int tid;

}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias("teacher")
public class Teacher {

  private int id;
  private String name;

  private List<Student> students;

}
Teacher getTeacher(@Param("tid") int id);

按结果嵌套查询

<select id="getTeacher" resultMap="teacherAndStudent">
  select t.id tid,t.name tname,s.id sid,s.name sname,s.tid stid
  from teacher t inner join student s on t.id = s.tid
  where t.id = 1;
</select>

<resultMap id="teacherAndStudent" type="teacher">
  <id property="id" column="tid"/>
  <result property="name" column="tname"/>

  <!--teacher对象中需要一个student对象的集合,类型就是student对象(别名)-->
  <collection property="students" ofType="student">

    <!--把从数据库中查到的字段值,一一对应的赋值给对象-->
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <result property="tid" column="stid"/>

  </collection>

</resultMap>
Teacher teacher = mapper.getTeacher(2);

System.out.println(teacher.toString());

按查询嵌套

<select id="getTeacher" resultMap="teacherAndStudent">
  select *
  from teacher
  where id = #{tid};
</select>

<resultMap id="teacherAndStudent" type="teacher">
  <!--teacher对象中需要一个student对象的集合,类型就是student对象(别名)
        每个对象的值来自于select查询的结果,把id传过去作为参数(它可以不用)-->
  <collection property="students" ofType="student" javaType="ArrayList"
              column="id" select="getStudentList"/>
</resultMap>

<select id="getStudentList" resultType="student">
  select * from student where tid = #{tid};
</select>

log4j可以完整打印出sql执行流程

Preparing: select * from teacher where id = ?;
Parameters: 2(Integer)
  
Preparing: select * from student where tid = ?;
Parameters: 2(Integer)
  
Total: 2
Total: 1

面试高频问题:

innodb底层原理,索引和索引优化


动态Sql

不要用java.sql.Date,直接用java.util.Date

真实开发中id是用UUID来生成的,保证唯一

String replace = UUID.randomUUID().toString().replace("-", "");

Student student = new Student(replace,"长歌",2);

mapper1.insert(student);

开始自动驼峰命名:允许字段名与实体类属性不同

<settings>
  <setting name="mapUnderscoreToCamelCase" value="true"></setting>
</settings>
//数据库中对应的字段名是student_name
private String studentName;//这是Student类中的属性

//但是最后插入成功了,就是因为我们设置开始了自动驼峰映射
mapper1.insert(new Student("13","12",3));

where在 最少有一个子元素成立 情况下,才会插入.会自动根据情况去掉and或or

<select id="getStudent" resultType="student">
  select * from student
  <where>
    <if test="id != null">
      id = #{id}
    </if>
    <if test="name != null">
      and name = #{name};
    </if>
  </where>

</select>

ohterwise相当于default

<select id="getStudent" resultType="student">
  select * from student
  <where>
    <!--有一个成立就直接break了-->
    <choose>
      <when test="id != null">
        id = #{id}
      </when>

      <when test="name != null">
        and name = #{name}
      </when>

      <otherwise>
        tid = #{tid};
      </otherwise>
    </choose>
  </where>

</select>

set在最少有一个子元素成立的情况下成立,会自动去除 , 号

<update id="updateStudent">
  update student
  <set>
    name = #{name},
  </set>
  <where>
    id = #{id};
  </where>

</update>

日志查看最终的sql语句

Preparing: update student SET name = ? WHERE id = ?;
int updateStudent(@Param("id") int id,@Param("name")String name,@Param("tid")int tid);

trim标签一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。

https://blog.csdn.net/wt_better/article/details/80992014#:~:text=mybatis

<!--update student SET name = ? where id = ? and tid = ?-->
<update id="updateStudent">
  update student
  <set>
    name = #{name},
  </set>

  <trim prefix="where" prefixOverrides="and">
    <if test="id != null">
      and id = #{id}
    </if>
    <if test="tid != null">
      and tid = #{tid}
    </if>
  </trim>

</update>
int updateStudent(@Param("id")int id, @Param("name")String name,@Param("tid")int tid);

mapper1.updateStudent(1,"天才第二步",1);
prefix给SQL语句拼接的前缀,为 trim 包含的内容加上前缀
suffix给SQL语句拼接的后缀,为 trim 包含的内容加上后缀
prefixOverrides去除 SQL 语句前面的关键字或字符,该关键字或者字符由 prefixOverrides 属性指定。
suffixOverrides去除 SQL 语句后面的关键字或者字符,该关键字或者字符由 suffixOverrides 属性指定。

sql片段:导入其他sql语句

<update id="updateStudent">
  update student
  <set>
    name = #{name},
  </set>

  <!--导入sql语句-->
  <include refid="sqlTest"/>

</update>

<sql id="sqlTest">
  <if test="id != null">
    <where>
      id = #{id};
    </where>
  </if>
</sql>

Foreach

<!--Preparing: update student SET name = ? WHERE id in ( ? , ? , ? )-->
<update id="updateStudent">
  update student
  <set>
    name = #{name},
  </set>

  <where>
    id in
    <!--where id in(id,id,id)
                open开始,separator分隔,close结束
            -->
    <foreach collection="ids" item="id"
             open="(" separator="," close=")">
      #{id}
    </foreach>
  </where>
</update>
int updateStudent(@Param("ids")ArrayList ids, @Param("name")String name);


ArrayList<Object> arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);

mapper1.updateStudent(arrayList,"天才第一步");

不要写慢sql


缓存:减少与数据库交互次数,减少系统开销

早些年会加一台memcached(缓存)服务器专门用来读取数据,做到读写分离.后来数据库方面又加入了**主从复制(依靠哨兵实现)**概念

小白都能懂的Mysql主从复制原理(原理+实操) - 知乎 (zhihu.com)

缓存机制包括:LRU(最近最少引用,默认60秒)和先进先出


一级缓存(本地缓存,SqlSession)

  1. 一次会话中(从SqlSession创建到close),

  2. 会把查询到的数据放在本地缓存中,

    下次再查询时就不走数据库,直接从本地缓存中拿

    Student studentById = mapper1.getStudentById(1);
    System.out.println(studentById);
    
    
    Student studentById1 = mapper1.getStudentById(1);
    System.out.println(studentById1);
    

    打开日志查看,就可以看到查询语句只输出了一遍

    Created connection 1955920234.
      
    Preparing: select * from student where id = ?
    Parameters: 1(Integer)
    Total: 1
      
    Student(id=1, name=天才第二步, tid=1)
    Student(id=1, name=天才第二步, tid=1)
    

    sqlSession.clearCache();//清理缓存

    Student studentById = mapper1.getStudentById(1);
    System.out.println(studentById);
    
    sqlSession.clearCache();//清理缓存
    
    Student studentById1 = mapper1.getStudentById(1);
    System.out.println(studentById1);
    
    Created connection 1955920234.
      
    Preparing: select * from student where id = ?
    Parameters: 1(Integer)
    Total: 1
    Student(id=1, name=天才第二步, tid=1)
    
    //查询了两遍  
    Preparing: select * from student where id = ?
    Parameters: 1(Integer)
    Total: 1
    Student(id=1, name=天才第二步, tid=1)
    

一级缓存相当于一个Map,

用debug时,可以看到mapper中有个methodCache对象,这就是一级缓存


二级缓存(全局缓存:namespace级别缓存)

工作机制:会话关闭后,一级缓存中的数据,会被保存到二级缓存中

<!--当前mapper开启二级缓存,只写一个cache会报错-->

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


<!-- cache只作用在当前这个mapper文件中的才有效,
     注解无用
 -->
<select id="getStudentById" resultType="student">
select * from student where id = #{id}
</select>

会话关闭后,再查询同一个语句,

调用二级缓存,

最后只有一次查询语句

SqlSession sqlSession = MyBatisUtil.getSqlSession();
SqlSession sqlSession1 = MyBatisUtil.getSqlSession();

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
StudentMapper mapper1 = sqlSession1.getMapper(StudentMapper.class);


mapper.getStudentById(1);
sqlSession.close();

mapper1.getStudentById(1);
sqlSession1.close();
Created connection 1332210474.
  
Preparing: select * from student where id = ?
Parameters: 1(Integer)
  
Total: 1

缓存原理

二级缓存没有找一级缓存,还没有再查询数据库

缓存就是为了提高查询的效率

<!-- useCache=false不使用缓存,最后就是查询两次 -->
<select id="getStudentById" resultType="student" useCache="false">

提示:二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。


自定义缓存

导入依赖

<!--这个依赖加上后,log4j就不用能了-->
<dependency>
  <groupId>org.mybatis.caches</groupId>
  <artifactId>mybatis-ehcache</artifactId>
  <version>1.2.2</version>
</dependency>

实现Cache类后,就是一个自定义缓存策略类,知道就行了

import org.apache.ibatis.cache.Cache;

public class MyCache implements Cache {

  public String getId() {
    return null;
  }

  public void putObject(Object key, Object value) {

  }

  public Object getObject(Object key) {
    return null;
  }

  public Object removeObject(Object key) {
    return null;
  }

  public void clear() {

  }

  public int getSize() {
    return 0;
  }
}

使用自定义缓存

<cache type="com.changGe.li.mapper.MyCache"/>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

helloses

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值