MyBatis学习日志

概念

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
接口的实现类由原来的Impl转变为了一个Mapper配置文件。
即:

public class StudentDaoImpl implements StudentDao{
    public List<Student> getStudent() {
        //1、获取Class类
        //2、获取数据库链接
        //3、写sql
        //4、获取Statement或者PrepareStatement
        //5、执行sql,获取结果
        return null;
    }
}

优化为一个xml文件,降低了程序的耦合性。如下:

<?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">
<!--namespace绑定一个对应的Dao/Mapper接口,id为所对应接口的方法,reslutType代表返回值类型-->
<mapper namespace="com.xia.dao.StudentDao">
    <select id="getStudent" resultType="com.kuang.pojo.Student">
        select * from school.student where id = #{id}
    </select>
</mapper>

调用查询工具

public class MybatisUtils {

    /*1、获取Class类
    2、获取数据库链接
    替换为了
    1、获取SqlSessionFactory*/
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession  getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

用法总结:

1、写工具类,为了所有的请求查询方法共用,优化代码,减少代码量
2、配置数据库链接资源文件,包含数据库ip,实例,账号,密码
3、写接口所对应的Mapper.xml(其实是Impl的马甲化身)
4、把Mapper注册到数据库配置文件中,通常是com/xia/

Mapper传递参数

每一个Mapper代表一个业务

public interface StudentDao {
    List<Student> getStudent();
    Student getStudentById(int StudentId);
    int addStudent(Student student);
    //map中键的名称与配置中sql中的参数保持一致即可传递成功
    int updateStudentById(Map<String,Object> map);
    int deleteStudent(int StudentId);
}
    <update id="updateStudentById" parameterType="map">
        update school.student set age = #{age} where id=#{studentId}
    </update>

测试类

    @Test
    public void updateStudent(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentDao mapper = sqlSession.getMapper(StudentDao.class);
        Map<String,Object> map = new HashMap<String, Object>();
        map.put("age",29);
        map.put("studentId",1);
        mapper.updateStudentById(map);
        sqlSession.commit();
        sqlSession.close();
    }

Map注册的三种方法

    <mappers>
<!--    一、    <mapper resource="com/xia/dao/StudentMapper.xml"/>-->
<!--   二、     <package name="com.xia.dao"/>                       -->
<!--   三、     <mapper class="comxia.dao.StudentMapper"></mapper>-->
    </mappers>

tip:第二种和第三种方法都必须让Mapper和Mapper.xml名字保持一致

日志工厂类

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

日志内容:

标准日志工厂:STDOUT_LOGGING在这里插入图片描述

LOG4J配置项:

log4j.rootLogger = DEBUG,Console,File
### 输出DEBUG 级别以上的日志到=控制台 ###
log4j.appender.Console = org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target = System.out
log4j.appender.Console.Threshold = DEBUG
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  - [ %p ]  %l %c %t - %m %n 
### 输出到日志文件 ###
log4j.appender.File=org.apache.log4j.RollingFileAppender
log4j.appender.File.File=${project}src\\main\\resources\\app.log
log4j.appender.File.MaxFileSize=10MB
log4j.appender.File.Threshold=ALL
log4j.appender.File.layout=org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern=[%p][%d{yyyy-MM-dd HH\:mm\:ss,SSS}][%c]%m%n
### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ${project}src\\main\\resources\\app.log
log4j.appender.D.Append = true
log4j.appender.D.Encoding=UTF8
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  - [ %p ]  %l %c %t - %m %n 
### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =${project}src\\main\\resources\\app.log
log4j.appender.E.Append = true
log4j.appender.E.Encoding=UTF8
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  - [ %p ]  %l %c %t - %m %n

复杂查询(使用Mybatis实现关联查询)

其根本就是实体与结果集的映射

数据库表设计:

CREATE TABLE `Teacher` (
  `id` int(11) unsigned NOT NULL COMMENT '老师Id',
  `name` varchar(100) DEFAULT NULL COMMENT '老师姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `student` (
  `id` int(11) NOT NULL COMMENT '学员Id',
  `name` varchar(100) DEFAULT NULL COMMENT '学生姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `tid` bigint(20) DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

① 一对多(多个学生关联一个老师):

查询实体中有另外一个Java类(如本例中学生中的Teacher),需要将另外一个实体类信息引到查询结果中。

select s.id sid,s.name sname,s.age sage,t.name tname,t.age tage from school.student s,school.Teacher t where s.tid = t.id

类设计
package com.xia.dao;

import com.xia.pojo.Student;
import java.util.List;

public interface StudentMapper {
    List<Student> getStudentAndTeacher();
}
package com.xia.pojo;

import lombok.*;
@ToString
@Setter
@Getter
@NoArgsConstructor
@EqualsAndHashCode
public class Student {
     private int id;
     private  String name;
     private int age;
     private Teacher teacher;
}
package com.xia.pojo;

import lombok.*;
@ToString
@Setter
@Getter
@NoArgsConstructor
@EqualsAndHashCode
public class Teacher {
    private int id;
    private String name;
    private int age;
}
拆分成两个查询
~xml
<select id="getStudentAndTeacher" resultMap="StudentAndTeacher2">
    select * from school.student
</select>
    <resultMap id="StudentAndTeacher2" type="Student">
        <association property="teacehr" column="tid" javaType="Teacher" select="getTeacher">

        </association>
    </resultMap>
    <select id="getTeacher" resultType="Teacher">
        select * from school.Teacher where id = #{id}
    </select>
关联查询
<mapper namespace="com.xia.dao.StudentMapper">
    <resultMap id="StudentAndTeacher" type="Student" >
        <result column="sid" property="id"/>
        <result column="sname" property="name"/>
        <result column="sage" property="age"/>
        <association property="teacher" javaType="Teacher">
            <result property="name" column="tname"/>
            <result property="age" column="tage"/>
        </association>
    </resultMap>

    <select id="getStudentAndTeacher" resultMap="StudentAndTeacher">
        select s.id sid,s.name sname,s.age sage,t.name tname,t.age tage from school.student s,school.Teacher t where s.tid = t.id;
    </select>
测试类
    @Test
    public void getSAndT(){
        /*
        1、查询出所有学生的ID
        2、根据学生的ID查找DUIYINGDE LAOSHI
         */
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> userAndTeacher1 = mapper.getStudentAndTeacher();
        for (Student userAndTeacher :userAndTeacher1
                ) {
            System.out.println(userAndTeacher);
        }
    }

2、多对一(一个老师包含多个学生)

如本例中,老师实体中包含多个学生。(查询实体中包含List)

select s.id sid,s.name sname,s.age sage,t.name tname,t.age tage from school.student s,school.Teacher t where s.tid = t.id**

类设计
import lombok.*;
/**
 * @author Guangpei Xia
 * @create 2021-06-14-15:58
 */
@ToString
@Setter
@Getter
@NoArgsConstructor
@EqualsAndHashCode
public class Student {
     private int id;
     private  String name;
     private int age;
     private int tid;
}
import lombok.*;
@ToString
@Setter
@Getter
@NoArgsConstructor
@EqualsAndHashCode
public class Teacher {
    private int id;
    private String name;
    private int age;
    private List<Student> students;
}
/**
 * @author Guangpei Xia
 * @create 2021-06-19-19:30
 */
public interface TeacherMapper {
    List<Teacher> getTeacher();
    Teacher getTeacherAndStudent(@Param("tid") int id);
    Teacher getTeacherAndStudent2(@Param("tid") int id);
}
拆分成两个查询
<!--from dao where function name = 'getTeacherAndStudent2'-->
<select id="getTeacherAndStudent2" resultMap="TeacherAndStudent2">
select * from school.teacher where id = #{tid}
</select>
<!--binding the resultMap of function-->
    <resultMap id="TeacherAndStudent2" type="Teacher">
    <!--结果集的一个扩展映射,通过select标签扩展子查询 -->
        <collection property="students" javaType="ArrayList" ofType="Student" select="selectStudentOfTeacher" column="id"/>
    </resultMap>
<select id="selectStudentOfTeacher" resultType="Student">
    select * from school.student where tid = #{tid}
</select>
关联查询
<select id="getTeacherAndStudent" resultMap="TeacherAndStudent">
    select t.id tid,t.name tname,t.age tage,s.id sid,s.name sname from school.teacher t left join school.student s on t.id = s.tid where t.id = #{tid}
</select>
<resultMap id="TeacherAndStudent" type="Teacher">
    <result property="id" column="tid"/>
    <result property="name" column="tname"/>
    <result property="age" column="tage"/>
    <collection property="students" ofType="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
    </collection>
</resultMap>
测试类
    @Test
    public void getTeacherAndStudent(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacherAndStudent = teacherMapper.getTeacherAndStudent(1);
        System.out.println(teacherAndStudent);
    }
    @Test
    public void getTeacherAndStudent2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacherAndStudent2 = mapper.getTeacherAndStudent2(1);
        System.out.println(teacherAndStudent2);
    }

全局配置项

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--    <settings>-->
<!--        <setting name="logImpl" value="LOG4J"/>-->
<!--    </settings>-->
    <typeAliases>
        <typeAlias type="com.xia.pojo.User" alias="User"></typeAlias>
        <typeAlias type="com.xia.pojo.Teacher" alias="Teacher"></typeAlias>
        <typeAlias type="com.xia.pojo.Student" alias="Student"></typeAlias>
    </typeAliases>
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/school"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--        <mapper resource="com/com.xia/dao/StudentMapper.xml"/>-->
        <package name="com.xia.dao"/>
        <!--        <mapper class="comxia.StudentMapper"></mapper>-->
    </mappers>
    <!--    ?useSSL=TRUE&amp;useUnicode=true&amp;chacterEncoding=utf-8-->
</configuration>

面试高频:
Mysql引擎
InnoDB底层原理
索引
索引优化

总结

javaType:指定实体类pojo中的属性
ofType:指定实体类中List或者集合中pojo类型。可以理解为javatype的明细项。本例一种实体只有一个student,用javatype映射类。本例二种,实体有List项,List的的泛型为Student。

动态SQL

DATABASE:

create table school.`blog`(
`id` BIGINT (20) UNSIGNED NOT NULL AUTO_INCREMENT,
`title` VARCHAR(100) NOT NULL COMMENT '标题',
`author` VARCHAR(30) NOT NULL COMMENT '作者',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP ,
`view` INT (30) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) 
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='博客';

类:


import lombok.*;
import java.util.Date;

/**
 * @author Guangpei Xia
 * @create 2021-06-20-12:12
 */
@ToString
@Setter
@Getter
@AllArgsConstructor
@EqualsAndHashCode
public class Blog {
    private String id;
    private String title;
    private String author;
    private String password;
    private Date create_time;
    private int view;
}
package com.xia.dao;

import com.xia.pojo.Blog;
import java.util.List;
import java.util.Map;
/**
 * @author Guangpei Xia
 * @create 2021-06-20-12:16
 */
public interface BlogMapper {
    int addBlog(Blog blog);
    List<Blog> queryBlogIf(Map map);
}

配置项:

    <!--1   if-->
    <select id="queryBlogIf" parameterType="map" resultType="Blog">
        select * from school.blog
        <where>
            <if test="id != null">
                id = #{id}
            </if>
            <if test="author != null">
                and author like #{author}
            </if>
        </where>
    </select>
    <!--2   choose when-->
    <select id="queryBlogChoose" parameterType="map" resultType="Blog">
        select * from school.blog
        <where>
            <choose>
                <when test="id != null">
                     id = #{id}
                </when>
                <when test="author != null and password !=null">
                    and (password = #{password} and author = #{author})
                </when>
            </choose>
        </where>
    </select>
    <!--3   set-->
    <update id="updateSet" parameterType="map">
        update school.blog
        <set>
            <if test="title !=null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author},
            </if>
            <if test="view != null">
                view = #{view}
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </update>

测试类:

    @Test
    public void queryByIf(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        Map map = new HashMap<String,String>();
        map.put("author","%"+"xia"+"%");
        List<Blog> blogs = mapper.queryBlogIf(map);
        for (Blog b:
             blogs) {
            System.out.println(blogs);
        }
    }

sql引用

上例中的mapper中的update配置可以改写成

    <!--3   set-->
    <sql id="updatesql">
        <set>
            <if test="title !=null">
                title = #{title},
            </if>
            <if test="author != null">
                author = #{author},
            </if>
            <if test="view != null">
                view = #{view}
            </if>
        </set>
        <where>
            id=#{id}
        </where>
    </sql>
    <update id="updateSet" parameterType="map">
        update school.blog
        <include refid="updatesql"/>
    </update>

foreach循环

select * from school.blog where id in (1,2);的问题

public interface BlogMapper {
    int addBlog(Blog blog);
    List<Blog> queryBlogIf(Map map);
    List<Blog> queryBlogChoose(Map map);
    int updateSet(Map map);
    List<Blog> selectTarget(Map map);
}
    <select id="selectTarget" parameterType="map" resultType="Blog">
        select * from school.blog
        <where>
            <foreach collection="ids" item="id" open="and (" close=")" separator="or">
                id = #{id}
             </foreach>
        </where>
    </select>
</mapper>

缓存

1、什么是缓存?
答:存在内存中临时数据,将用户经常访问的数据放在缓存中,而不用从磁盘上查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题。
2、为什么使用缓存?
答:减少和数据库的交互次数,减少系统开销,提高系统效率
3、什么样的数据能使用缓存?
答:经常查询且不经常改变的数据。(反之,同理)

三高:高可用、高并发、高性能
①100个用户Tomcat:客户机→服务器→数据库
②1000个用户:客户机→N个服务器→数据库,读写问题(并发)?
③10000个用户:客户机→N个服务器→缓存服务器(读)→数据库(写)
④100000个用户:客户机→N个服务器→缓存服务器(读)→N个数据库(主、从),分布式(CAP)主从复制、读写分离。

Mybatis缓存

一级缓存(默认自动开启)

在应用运行过程中,我们有可能在一次数据库会话中,执行多次查询条件完全相同的SQL,MyBatis提供了一级缓存的方案优化这部分场景,如果是相同的SQL语句,会优先命中一级缓存,避免直接对数据库进行查询,提高性能。

每个SqlSession中持有了Executor,每个Executor中有一个LocalCache。当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询,如果缓存命中的话,直接返回结果给用户,如果缓存没有命中的话,查询数据库,结果写入Local Cache,最后返回结果给用户。

    @Test
    public void getUserById(){
     SqlSession sqlSession = MybatisUtils.getSqlSession();
     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     User user = userMapper.getUserById(1);
     System.out.println(user);
     System.out.println("========================================================================");
     User user2 = userMapper.getUserById(2);
     System.out.println(user2);
     //日志中只有一次执行sql记录。
 }

修改了其他数据会导致缓存清空:

    @Test
    public void getUserById(){
     SqlSession sqlSession = MybatisUtils.getSqlSession();
     UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     User user = userMapper.getUserById(1);
     System.out.println(user);
     //更新了其他2数据
     Map map = new HashMap();
     map.put("id",2);
     map.put("pwd",290909);
     userMapper.updateSet(map);
     System.out.println("========================================================================");
     //再次查询1数据的时候会清除缓存,从新从数据库查询
     User user2 = userMapper.getUserById(1);
     System.out.println(user2);
 }

手动提交也会清理缓存

二级缓存(Cache类自定义,基于namespace的)

开启全局缓存

<!--mybatis-config中配置-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
<!--在当前Mapper中,先进先出策略,每隔60m刷新,最多存512个引用,返回的对象是只读的-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
	<!--也可以在当前执行的方法中开启缓存-->
    <select id="getUserById" resultType="User" useCache="true">
        select * from school.user where id =#{id}
    </select>
    <!--关闭刷新缓存,数据库调优的时候会用-->
        <update id="updateSet" parameterType="map" flushCache="false">
        update school.user
        <include refid="updatesql"/>
    </update>

测试:

 @Test
 public void getUserTwoCache(){
  SqlSession sqlSession1 = MybatisUtils.getSqlSession();
  SqlSession sqlSession2 = MybatisUtils.getSqlSession();
  UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
  UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);

  User user1 = userMapper1.getUserById(1);
  System.out.println(user1);
  sqlSession1.close();
  //因为一个会话关闭了,会把当前数据缓存到二级缓存中,另外一个会话再次查询的时候就回去缓存中查找。
  User user2 = userMapper2.getUserById(1);
  System.out.println(user2);
  sqlSession2.close();

结果
在这里插入图片描述

Mybatis进阶

Mybatis是通过建造器设计模式解析XML配置的每个子节点。
其中将Mybatis的全局配置文件((mapUnderscoreToCamelCase)配置、链接、事务、缓存)用XMLConfigBuilder读取并加载。每一个CRUD都解析成MapperStatement,放到configuration中。
其中Mapper的解析时是XMLMapperBuilder,会把每个Mapper构成一个HashMap<类,MapperProxyFactory>存储,通过getMap(),可以调用。解析resultMap提供返回值结果映射。Mapper的配置(二级缓存相关)的解析用到了装饰器设计模式。
而SQL的解析是XMLStatementBuilder,又采用组合设计模式遍历所有子节点,将不同节点解析成不同的Node,返回MixedSqlNode。最终每个select解析成MappedStatement(sqlSource,sql,resultMap,id)
执行SQL时,得到的SqlSession又采用了门面设计模式。SqlSession中封装了Executor执行器(配置默认的执行器),负责改(增删改)和查(SelectOne和SelectList)、(提交、关闭执行器、维护一级缓存)。最后就调用每个noded的apply方法组成SQL。
Mybatis的执行器有哪几种:SIMPLE、REUSE、BATCH,又通过父类BaseExecutor来实现一级缓存的查询,又通过装饰器设计模式的CacheExecutor类封装delete。创建执行器时,通过读取全局配置类中的settings来创建SqlSession,如果开启了二级缓存,又会CatcheExecutor包装BaseExecutor负责二级缓存。如果未开启,那么最外层的缓存是BaseExecutor。而后StatementHandler会执行参数解析(ParameterHandler),映射和结果映射。(ResultSetHandler)

        // 数据源 执行器  DefaultSqlSession 2
        SqlSession session = sqlSessionFactory.openSession();

SQL执行 进阶。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值