MyBatis学习笔记

 

配置解析

1.核心配置

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息

configuration(配置)

2.环境配置(environments)

 在xml中,所有的标签都有其顺序,不可打乱
 properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?

MyBatis 可以配置成适应多种环境

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

每个数据库对应一个 SqlSessionFactory 实例

 <environments default="development">
     <environment id="development">
         <!--transactionManager事务管理,默认为JDBC,共有两种,另一种为MANAGED,不常用-->
         <transactionManager type="JDBC"/>
     <!--有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
         没有连接池/有连接池/正常连接
     -->
         <dataSource type="POOLED">
             <property name="driver" value="${driver}"/>
             <!--jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8-->
             <property name="url" value="${url}"/>
             <property name="username" value="${username}"/>
             <property name="password" value="${password}"/>
         </dataSource>
     </environment>
 </environments>

3.属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换

 driver=com.mysql.cj.jdbc.Driver
 url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
 username=root
 password=123
 <properties resource="db.properties"/>
 <!--  也能额外添加一些属性,优先使用外部配置文件里面的属性-->     
 <properties><property name="username" value="root"/>
         <property name="password" value="123"/>
   </properties>

4.类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字, 意在降低冗余的全限定类名书写

 <!--可以给实体类自定义别名-->
 <typeAliases>
 <typeAlias type="com.liu.entity.User" alias="User"/>
 </typeAliases>

通过包来取别名,默认为这个类的类名,不分大小写,建议用小写

 <typeAliases>
 <package name="com.liu.entity"/>
 </typeAliases>

可以通过注解来更改别名,不建议使用这种方式

 @Alias("test")
 public interface UserDao {

5.映射器(mappers)

     <!--每一个Mapper.xml文件都要在MyBatis核心配置文件中注册-->
     <!-- 使用相对于类路径的资源引用 -->
     <mappers>
         <mapper resource="com/liu/dao/UserMapper.xml"/>
     </mappers>
     <!--使用映射器接口实现类的完全限定类名-->
     <!-- 注意:接口和他的配置文件必须同名-->
     <!--接口和他的配置文件必须在同一包下-->
     <!-- 使用映射器接口实现类的完全限定类名 -->
       <mapper class="com.liu.dao.UserMapper"/>
     <!-- 将包内的映射器接口实现全部注册为映射器 -->
       包注入同上
        <package name="com.liu.dao"/>

6.作用域(Scope)和生命周期

 

作用域、生命周期类别是童年的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder:

  1. 创建创作了SqlSessionFactory,就不再需要它了,因此SqlSessionFactoryBuilder实例的最佳作用是作用域(也就是局部方法方法)。

SqlSessionFactory:

  1. 数据库连接池

  2. SqlSession 应用游戏被创建就应该在运行整个过程中一直存在,没有任何环境破坏或创建另一个实例。

  3. 因此SqlSessionFactory 的最佳作用域是应用作用域。 04. 最简单的就是使用单例模式或者静态单例模式。

SqlSession:

  1. 连接到连接池的一个请求;

  2. 每个线程都应该有它自己的SqlSession实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳作用是域或方法作用域。绝对不能将SqlSession实例的引用放出在一个类的动态域,甚至一个类的实例变量也不行。

  3. 用完后需要关闭,否则资源被占用。

     try (SqlSession session = sqlSessionFactory.openSession()) {
       // 你的应用逻辑代码
     }

 

在所有代码中都遵循这种使用模式,可以保证所有数据库资源都被正确地关闭。

这里的每个Mapper,就代表一个具体的业务!

分页

使用Limit分页

使用Mybatis实现分页,核心是SQL

 语法:select * from mybatis.user limit indexpage,pageSize
         select * from mybatis.user limit 3 【0,3】

实现

1.在接口内编写方法,使用map传递参数

 List<User> getLimit(Map<Object,Object> map);

2.实现方法

     <!--结果集映射  解决实体类属性和数据库字段不相同的问题-->
     <resultMap id="UserMap" type="User">
         <!--column代表数据库中的字段 property代表实体类中的属性-->
         <result column="pwd" property="password"/>
     </resultMap> 
 ​
 <select id="getLimit" parameterType="map" resultMap="UserMap">
         select * from mybatis.user limit #{startIndex},#{PageSize}
     </select>

3.测试代码

  @Test
     public void Limit(){
         SqlSession sqlSession = MyBatisUtil.getSqlSession();
         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
         HashMap<Object, Object> map = new HashMap<Object, Object>();
         map.put("startIndex",0);
         map.put("PageSize",2);
         List<User> limit = mapper.getLimit(map);
         for (User user: limit){
             System.out.println(user);
         }
         sqlSession.close();
     }

RowBounds分页

不再使用SQL实现分页

接口

     //使用RowRounds实现分页
     List<User> getRowBounds();

实现

    <select id="getRowBounds"  resultMap="UserMap">
         select * from mybatis.user
     </select>

测试

 ​
     @Test
     public void getRowBounds(){
         SqlSession sqlSession = MyBatisUtil.getSqlSession();
         //RowBounds实现
         RowBounds rowBounds = new RowBounds(0, 2);
         //基于java代码实现分页
         List<User> us = sqlSession.selectList("com.liu.dao.UserMapper.getRowBounds", null, rowBounds);
         for (User user:us){
             System.out.println(user);
         }
         sqlSession.close();
     }

使用注解开发

CRUD

1.注解在接口上实现

     @Select("select * from mybatis.user")
     List<User> gtelist();
 ​
     //方法存在多个参数,所有参数前面必须加上@Param注解
     @Select("select * from mybatis.user where id = #{id}")
     User ById(@Param("id") int id);
     
      @Insert("insert into mybatis.user(id,username,pwd) values(#{id},#{username},#{pwd})")
     int add(User user);
    
     @Delete("delete from mybatis.user where id = #{id}")
     int del(@Param("id") int id);
 ​
      @Update("update mybatis.user set username=#{username},pwd=#{pwd} where id=#{id}")
     int upd(User user);

关于@Param()注解

基本类型的参数或者String类型的参数需要加上

引用类型不需要加

如果只有一个基本类型的话,可以忽略,但是建议加上

我们在Sql中引用的,就是@param中设置的属性名

2.需要在核心配置文件中绑定接口

注意:我们必须要将接口绑定到我们的核心配置文件中

    <mappers>
         <mapper class="com.liu.dao.UserMapper"/>
     </mappers>

3.测试

 public void list(){
 SqlSession sqlSession = MyBatisUtil.getSqlSession();
 UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 //查询
 List<User> gtelist = mapper.gtelist();
 for (User user : gtelist) {
 System.out.println(user);
 }
 ​
 //按ID查询
 User user = mapper.ById(2);
 System.out.println(user);
 ​
 //修改
 mapper.upd(new User(9,"周怡","666999"));
 ​
 //删除
 mapper.del(10);
 ​
 //添加
 mapper.add(new User(10,"周怡","666"));
 ​
 sqlSession.close();
 }

本质:反射机制实现

底层:动态代理

 public static SqlSession getSqlSession(){
     //开启自动提交事务
     return sqlSessionFactory.openSession(true);
 }

Lombok

Project Lombok是一个java库,它可以自动插入编辑器和构建工具,为java增添乐趣。永远不要再编写另一个getter或equals方法,只要有一个注释,您的类就有一个功能齐全的构建器、自动记录变量等等。

使用步骤

1.在IDEA中安装Lombok插件

2.在项目中导入Lombok的jar包

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
    <scope>provided</scope>
</dependency>

3在实体类上加注解

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    private int id;
    private String username;
    private String pwd;
}
 @Getter and @Setter//
 @FieldNameConstants //字段,属性,常量
 @ToString//
 @EqualsAndHashCode//
 @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor//
 @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
 @Data //
 @Builder
 @SuperBuilder
 @Singular
 @Delegate
 @Value
 @Accessors
 @Wither
 @With
 @SneakyThrows
 @val
 @var
 experimental @var
 @UtilityClass
 Lombok config system
 Code inspections
 Refactoring actions (lombok and delombok)

@Data:无参构造,get,set,hashcode,tostring,equals

@AllArgsConstructor:有参构造

@NoArgsConstructor:无参构造

多对一

 

多个学生,对应一个老师

对学生而言,关联,多个学生,关联一个老师【多对一】

对老师而言,集合,一个老师,又很多学生,【一对多】

 CREATE TABLE `teacher` (
   `id` INT(10) NOT NULL,
   `name` VARCHAR(30) DEFAULT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=INNODB DEFAULT CHARSET=utf8
 ​
 INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老师'); 
 teacher
 CREATE TABLE `student` (
   `id` INT(10) NOT NULL,
   `name` VARCHAR(30) DEFAULT NULL,
   `tid` INT(10) DEFAULT NULL,
   PRIMARY KEY (`id`),
   KEY `fktid` (`tid`),
   CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
 ) ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `student` (`id`, `name`, `tid`) VALUES (1, 小明, 1); 
 INSERT INTO `student` (`id`, `name`, `tid`) VALUES (2, '小红', 1); 
 INSERT INTO `student` (`id`, `name`, `tid`) VALUES (3, '小张', 1); 
 INSERT INTO `student` (`id`, `name`, `tid`) VALUES (4, '小李', 1); 
 INSERT INTO `student` (`id`, `name`, `tid`) VALUES (5, '小王', 1);

 

Mybatis核心配置中结果映射(resultMap)

 

测试环境搭建

1.导入Lombok

2.新建实体类

3.建立Mapper接口

4.建立Mapper.xml文件

5.在核心配置文件中绑定注册我们的Mapper或者xml文件

6.测试查询是否成功

按照查询嵌套处理

实体

 private int id;
 private String name;
 //多个学生关联一个老师
 private Teacher teacher;

 

接口

 List<Student> getStudent();

实现

 <!--
 思路:
 1.先查出所有的学生信息
 2.根据查询出来的学生的tid,查询对应的老师,子查询
 -->
 <select id="getStudent" resultMap="StudentTeacher">
 select * from mybatis.student
 </select>
 ​
 <resultMap id="StudentTeacher" type="Student">
 <!--result只能处理简单的属性-->
 <result property="id" column="id"/>
 <result property="name" column="name"/>
 <!--association用来处理复杂的属性 对象使用:association  集合使用:collection -->
     
 <association property="teacher" column="tid" javaType="teacher" select="getById"/>
 </resultMap>
 ​
 <select id="getById" resultType="teacher">
 select * from mybatis.teacher where id =#{id}
 </select>

按照结果嵌套处理

实现

<!--给SQL语句中查询的字段取别名-->
<select id="getStudent2" resultMap="TeacherName">
SELECT s.id sid,s.name sname,t.name tname FROM mybatis.student s,teacher t WHERE s.tid=t.id
</select>


<resultMap id="TeacherName" type="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
    
<association property="teacher" javaType="teacher">
    <result property="name" column="tname"/>
</association>
    
</resultMap

回顾多对一查询方式

  • 子查询

  • 联表查询

一对多

按照结果嵌套处理

实体类

    private int id;
    private String name;
    //一个老师拥有多个学生
    private List<Student> students;

接口

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

实现

<!--按结果嵌套查询-->
<select id="getTeacher" resultMap="st">
	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="st" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <!-- association用来处理复杂的属性 对象使用:association  集合使用:collection -->
    <!-- javaType: 指定属性的类型   集合中的泛型信息用ofType获取-->
    <collection property="students"ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <result property="tid" column="tid"/>
    </collection>
 </resultMap>

按照查询嵌套处理

实体和方法不变

  1. 关联 - association 多对一

  2. 集合 - collection 一对多

  3. javaType & ofTyp

  4. javaType用来指定实体类中属性的类

  5. ofType用来指定映射到List 或者集合中的pojo类型,泛型中的约束类型

实现

 <select id="getST" resultMap="tt">
     select * from mybatis.teacher where id=#{tid}
 </select>
 ​
 ​
 <resultMap id="tt" type="Teacher">
     <collection property="students"                             javaType="ArrayList" ofType="Student"               select="getStudentTeacher" column="id">
     </collection>
 </resultMap>
 ​
 ​
 <select id="getStudentTeacher" resultType="Student">
     SELECT * FROM student WHERE tid=#{tid}
 </select>

动态SQL

什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句

环境搭建

 CREATE TABLE `blog`(
 `id` VARCHAR(50) NOT NULL COMMENT 博客id,
 `title` VARCHAR(100) NOT NULL COMMENT 博客标题,
 `author` VARCHAR(30) NOT NULL COMMENT 博客作者,
 `create_time` DATETIME NOT NULL COMMENT 创建时间,
 `views` INT(30) NOT NULL COMMENT 浏览量
 )ENGINE=INNODB DEFAULT CHARSET=utf8

实体

 import java.util.Date;
 @Data
 public class Blog {
     private String id;
     private String title;
     private String author;
     private Date createTime;//属性名和字段名不一致create_time
     private int views;
 }
     <!--    是否开启驼峰命名自动映射 -->
         <setting name="mapUnderscoreToCamelCase" value="true"/>

ID工具类

 public class IDUtils {
 public static String getID(){
 return UUID.randomUUID().toString().replaceAll("-","");
 }

IF(test)

接口

     List<Blog> queryBlog(Map map);

实现

  <select id="queryBlog" parameterType="map" resultType="Blog">
         SELECT * FROM blog
         <where>
             <if test="title != null">
                   title like #{title}
             </if>
             <if test="author">
                 and author like #{author}
             </if>
         </where>
     </select>

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

测试

 @Test
 public void query(){
     SqlSession sqlSession =                             MyBatisUtil.getSqlSession();
     BlogMapper mapper =                                  sqlSession.getMapper(BlogMapper.class);
     HashMap map = new HashMap();
     //map.put("title","CRUD");
     map.put("author","%十%");
     mapper.queryBlog(map);
     sqlSession.close();
 }

Choos(when、otherwise)

 <select id="query" parameterType="map" resultType="blog">
     select * from mybatis.blog
     <where>
         <choose>
             <when test="title != null">
                 title =#{title}
             </when>
             <when test="author != null">
                 and author=#{author}
             </when>
             <otherwise>
                 views=#{views}
             </otherwise>
         </choose>
     </where>
 </select>

Update( set,if,test )

使用Map传参

 <update id="updBlog" parameterType="map">
         update mybatis.blog
     <set>
         <if test="title != null">
             title=#{title},
         </if>
         <if test="author !=null">
             author=#{author}
         </if>
     </set>
     where id=#{id}
 </update>

trim

指定前缀,去除AND和OR

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

指定后缀,去除逗号

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

所谓的动态SQL,本质上还是SQL语句,只是我们可以在SQL层面,去执行一个逻辑代码

Foreach

格式

item 集合项

index 索引

collection 指定需要遍历的集合

open 拼接头的字符串

close 拼接尾的字符串

separator 分隔符

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

案例

SQL语句

 select * from mybatis.blog where (id=1 or id=2 or id=3)

接口

 List<Blog> BlogForeach(Map map);

 

实现

 <select id="BlogForeach" parameterType="map" resultType="blog">
         select * from mybatis.blog
     <where>
         <foreach collection="list" item="id" open="(" close=")" separator="or">
            id=#{id}
         </foreach>
     </where>
 </select>

测试

 public void Foreach(){
     SqlSession sqlSession = MyBatisUtil.getSqlSession();
     BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
     HashMap map = new HashMap();
     ArrayList<Object> list = new ArrayList<Object>();
     list.add(2);
     map.put("list",list);
     List<Blog> blogs = mapper.BlogForeach(map);
     for (Blog blog : blogs) {
         System.out.println(blog);
     }
     sqlSession.close();
 }

SQL片段

有时候,我们会将一部分一些功能的部分抽取出来,方便复用

1.使用SQL标签抽取需要复用的部分

 <sql id="query-choose">
     <choose>
         <when test="title != null">
             title =#{title}
         </when>
         <when test="author != null">
             and author=#{author}
         </when>
         <otherwise>
             views=#{views}
         </otherwise>
     </choose>
 </sql>

2.在需要使用的地方使用include标签引用即可

 <select id="query" parameterType="map" resultType="blog">
     select * from mybatis.blog
     <where>
         <include refid="query-choose"></include>
     </where>
 </select>

主要事项

  • 最好基于单标来定义SQL片段

  • 不要存在where标签

动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了

缓存

简介

  • 查询:连接数据库,秏资源

  • 一次查询的结果,给它暂存到可以直接取到的地方---->内存:缓存

  • 我们再次查询相同数据的时候,直接走缓存,就不用走数据库了

1.什么是缓存?

  • 存在内存中的临时数据

  • 将用户经常查询的数据放在缓存中,用户去查询数据就不用从数据库中查询,而是从缓存中查询,提高查询效率,解决高并发系统的性能问题

2.为什么使用缓存?

  • 减少和数据库的交互次数,减少系统开销,提高系统效率

3.什么样的数据能使用缓存?

  • 经常查询并且不经常改变的数据

MyBatis缓存

MyBatis包含了一个非常强大的查询缓存特性,它可以非常方便的定制和配置缓存,缓存可以极大的提升查询效率

MyBatis系统中默认定义了两级缓存:一级缓存二级缓存

  • 默认情况下,只有一级缓存开启(sqlsession级别的缓存,也称为本地缓存)

  • 二级缓存需要手动开启和配置,它是基于namespace级别的缓存

  • 为了提高扩展性,MyBatis定义了缓存接口Cache,我们可以通过实现Cache接口来自定义二级缓存

一级缓存

一级缓存也叫本地缓存:sqlsession

  • 与数据库同一次会话期间查询到的数据会放在本地缓存中

  • 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

测试

  1. 开启日志

  2. 测试再一次session中查询两次相同的记录

     @Test
     public void query(){
         SqlSession sqlSession = MyBatisUtil.getSqlSession();
         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
         User user = mapper.queryById(1);
         System.out.println(user);
         System.out.println("===============================");
         User user1 = mapper.queryById(1);
         System.out.println(user1);
         sqlSession.close();
     }
  3. 查看日志输出:SQL语句只走了一次,第二次的数据是直接从Sqlsession中获取的

 

缓存失效的情况

  1. 查询不同的数据

  2. 增删改操作,可能会改变原来的数据,所以必定会刷新缓存

  3.  

  4. 查询不同的Mapper

  5. 手动清理缓存

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

小结:一级缓存默认开启,只在一次sqlsession中有效,也就是拿到连接到关闭连接这个区间有效

二级缓存

 

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存

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

工作机制

  • 一个会话查询一条数据,这条数据就会被放在当前会话的一级缓存中

  • 如果当前会话关闭了,这个会话对应的一级缓存就没了,但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中

  • 新的会话查询信息,就可以从二级缓存中获取内容

  • 不同的Mapper查出的数据会放在自己对应的缓存(map)中

步骤:

  1. 开启全局缓存

     <!-- 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。-->
     <setting name="cacheEnabled" value="true"/>
  2. 在要使用二级缓存的Mapper中开启二级缓存

         <!--开启二级缓存-->
         <cache/>

    也可以在里面自定义一些参数

     <cache
     eviction="FIFO"
     flushInterval="60000"
     size="512"
     readOnly="true"/>
  3. 测试

      public void Two(){
             //创建两个sqlsession对象,执行同一个方法查询同一条数据,当第一个sqlsession缓存关闭的时候
             //一级缓存中的数据会被保存到二级缓存中,下一个方法可以直接从缓存中获取数据
             SqlSession sqlSession = MyBatisUtil.getSqlSession();
             UserMapper mapper = sqlSession.getMapper(UserMapper.class);
             User user = mapper.queryById(9);
             System.out.println(user);
             sqlSession.close();
     ​
             SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
             UserMapper mapper2= sqlSession2.getMapper(UserMapper.class);
             User user2 = mapper2.queryById(9);
             System.out.println(user2);
             System.out.println(user==user2);
             sqlSession2.close();
         }

    序列化实体类,有时候会报错

     

     public class User implements Serializable

小结:

  • 只要开启了二级缓存,在同一个Mapper下就有效

  • 所有的数据都会先放在一级缓存中

  • 只有当会话提交,或者关闭的时候,才回提交到二级缓存中去

第一次写博客,随便写写,记录一下之前学习MyBatis时候的笔记,还有一点稍候补上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值