MyBatis基础原理

MyBatis持久层框架

数据持久化:将程序的数据在持久状态和瞬时状态转化的过程

官方文档: MyBatis中文网

内存:断电即失

第一个程序

TeacherMapper接口,有一个获取所有Teacher的方法待实现

public interface TeacherMapper {

  List<Teacher> getTeacherList();

}
  1. 导入依赖(操作数据库时,还需要导入一个mysql-connector-java的依赖)
<dependency>
  <groupId>org.mybatisgroupId>
  <artifactId>mybatisartifactId>
  <version>3.5.7version>
dependency>

  1. mybatis.xml文件连接数据库

jdbc.properties

driver=com.mysql.cj.jdbc.Driver
username=root
password=root
url=jdbc:mysql:///mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true&serverTimezone=UTC

官方配置文件模板


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            dataSource>
        environment>
    environments>
    <mappers>
        <mapper resource="org/mybatis/example/BlogMapper.xml"/>
    mappers>
configuration>

  1. 原本的dao接口实现类daoImpl,现在变成一个和接口同名的mapper.xml文件

    TeacherMapper.xml,就是把这里面的configuration全部换成mapper


DOCTYPE mapper
          PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
          "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.changGe.li.mapper.TeacherMapper">

    
    <select id="getTeacherList" resultType="com.changGe.li.pojo.Teacher">
        select * from teacher;
    select>

mapper>

  1. mybatis-config.xml配置文件中注册TeacherMapper的实现类(TeacherMapper.xml)

mybatis-config.xml

注意:文件用/号来分隔,类用.号来分隔


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>

    
    <properties resource="jdbc.properties">properties>

    
    <typeAliases>
        <typeAlias type="com.changGe.li.pojo.Teacher" alias="teacher"/>
    typeAliases>

    
    <environments default="development">
        <environment id="development">
            
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                
                
                
                <property name="driver" value="${driver}"/>
                <property name="url" value="jdbc:mysql:///mybatis?useUnicode=true&ampcharacterEncoding=utf8"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            dataSource>
        environment>
    environments>

    
    <mappers>
        <mapper resource="com/changGe/li/mapper/StudentMapper.xml"/>
        <mapper resource="com/changGe/li/mapper/TeacherMapper.xml"/>
    mappers>

configuration>

xml中的&要加上amp;来表示

<property name="url" value="jdbc:mysql:///mybatis?useUnicode=true&ampcharacterEncoding=utf8"/>

  1. Mybatis工具类,获取sqlSession(数据库操作)对象
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisUtil {

  private static SqlSessionFactory sqlSessionFactory;

  static {

    try {
      //读取配置文件
      InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-onfig.xml");

      //工厂构建类 构建 得到sql会话工厂
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    } catch (IOException e) {
      e.printStackTrace();
    }

  }

  //用工厂类来获取执行sql操作的对象
  public static SqlSession getSqlSession(){
    //打开会话,也就是开启和数据库的连接
    return sqlSessionFactory.openSession();
  }

}

  1. 用SqlSession对象来获取实现类对象,然后执行方法
@Test
public void test(){
  //工具类获取SqlSession对象
  SqlSession sqlSession = MyBatisUtil.getSqlSession();

  //底层用反射机制,获取了接口TeacherMapper的类对象
  TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

  //接口被实现类TeacherMapper.xml实现了,调用接口就是调用实现类的方法
  List<Teacher> teacherList = mapper.getTeacherList();

  id:1,姓名是:秦老师
  for (Teacher teacher : teacherList) {
    System.out.println("id:"+teacher.getId()+",姓名是:"+teacher.getName());
  }
  

  //一定要关闭会话,免得占用资源
  sqlSession.close();

}
  1. 早年间官方给我们提供了一些默认的方法,可以通过传入方法路径,来直接执行.不过没有第一种方法好理解
@Test
public void test(){
  
  SqlSession sqlSession = MyBatisUtil.getSqlSession();

  TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);


  //sql会话时,直接调用接口中的方法
  List<Teacher> teacherList = sqlSession.selectList("com.changGe.li.mapper.TeacherMapper.getTeacherList");

  //id:1,姓名是:秦老师
  for (Teacher teacher : teacherList) {
    System.out.println("id:"+teacher.getId()+",姓名是:"+teacher.getName());
  }

  sqlSession.close();
}


注意事项

如果找不到对象,可能因为maven是找不到mapper.xml,用下面的代码,让maven在构建项目时把文件构建进来

<build>

  <resources>
    <resource>
      
      <directory>src/main/javadirectory>
      <includes>
        <include>**/*.propertiesinclude>
        <include>**/*.xmlinclude>
      includes>
      <filtering>falsefiltering>
    resource>
  resources>
build>

好像说mapper.xml中的中文注释也会出问题,可能是要把文件格式改成UTF-8


官方名词概念

三大对象

在后面的生命周期和作用域会讲解

完全限定名

就是可以让mybatis直接打到资源的一个精确名字,如com.changGe.li.pojo.Teacher


CRUD

对象中的值可以直接拿来用


<select id="getTeacherByIdAndName" resultType="teacher" parameterType="map">
  select * from teacher where id=#{id} and name = #{name};
select>
@Test
public void test(){
  SqlSession sqlSession = MyBatisUtil.getSqlSession();

  TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

  HashMap<String, String> map = new HashMap<String, String>();

  map.put("id","1");
  map.put("name","秦老师");
  Teacher teacherByIdAndName = mapper.getTeacherByIdAndName(map);
  System.out.println(teacherByIdAndName);
  
  sqlSession.close();

}

增删改一定要提交事务sqlsession.commit();

<insert id="addTeacher" parameterType="teacher">
  insert into teacher values(#{id},#{name})
insert>

@Test
public void test(){
  SqlSession sqlSession = MyBatisUtil.getSqlSession();

  TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

  Teacher teacher = new Teacher(2,"李长歌");

  mapper.addTeacher(teacher);

  //不提交事务,不更新数据
  sqlSession.commit();
  
  sqlSession.close();

}

获取更新时记录的key(slelectKey)

https://blog.csdn.net/kongkongyanan/article/details/86096657

<insert id="insertStudent">
  
  <selectKey keyProperty="id" order="AFTER">
    SELECT LAST_INSERT_ID() AS ID
  selectKey>
  insert into student values(#{id},#{name},#{tid})

insert>
Student student = new Student();
student.setName("张三");
student.setTid(1);

mapper.insertStudent(student);

System.out.println(student.getId());

模糊查询

select * from teacher where name like concat('%',#{name},'%');
Teacher teacher = mapper.getTeacherByName("长歌");

配置优化

xml规定了所有标签的顺序,必须完全符合规定

properties,settings,

typeAliases,typeHandlers,

objectFactory,objectWrapperFactory,

reflectorFactory,plugins,

environments,databaseIdProvider,

mappers

多环境配置

<environments default="test">
  <environment id="development">
    
    <transactionManager type="JDBC"/>
    
    <dataSource type="POOLED">
    dataSource>
  environment>

  <environment id="test">
    <transactionManager type="jdbc"/>
    <dataSource type="pooled">
      <property name="driver" value="${driver}"/>
      <property name="url" value="${url}"/>
      <property name="username" value="${username}"/>
      <property name="password" value="${password}"/>
    dataSource>
  environment>
environments>

属性优化properties 替换需要动态配置的属性值

<properties resource="jdbc.properties">
  <property name="username" value="root"/>
properties>

<dataSource type="pooled">
  
  <property name="username" value="${username}"/>
dataSource>

类型别名: 降低冗余的全限定类名书写

<typeAliases>
  <typeAlias type="com.changGe.li.pojo.Teacher" alias="teacher">typeAlias>

  
  <package name="com.changGe.li.pojo"/>
typeAliases>

<select id="getTeacherByName" resultType="teacher">

注解设置别名

@Alias("teacher")
public class Teacher {}

但是需要在mybatis-config.xml中,配置在哪个包下扫描注解

<typeAliases>
  <package name="com.changGe.li.pojo"/>
typeAliases>

系统默认别名

基本类型加_,包装类是对应的基本类型,集合首字母小写

int 别名是 _int,Integer = int, Map = map

<select id="getTeacherByName" resultType="teacher" parameterType="_int">

settings系统配置:改变 MyBatis 的运行时行为

可以设置如日志,缓存懒加载

slf4j依赖

<dependency>
  <groupId>org.slf4jgroupId>
  <artifactId>slf4j-apiartifactId>
  <version>2.0.0-alpha4version>
dependency>
<settings>
  
  <setting name="logImpl" value="SLF4J"/>
  
  <setting name="cacheEnabled" value="true"/>
  
  <setting name="lazyLoadingEnabled" value="true"/>
settings>

MapperGesitry注册,映射器: 直接告诉 MyBatis 到哪里去找映射文件

当用实现类或包注册时,要求:实现类和配置文件与接口同名,且同包!

<mappers>
  
  
  <mapper class="com.changGe.li.mapper.TeacherMapper"/>
  
  <package name="com/changGe/li/mapper"/>
mappers>

如果想分离,要在resource包下创建和接口相同结构的包.

因为mybatis的resource是通过classpath来找文件的


生命周期和作用域

错误的使用可能会出现严重的并发问题
MyBatis基础原理_第1张图片

  1. 程序开始,配置文件被sqlsessionfactoryBuilder构建,
  2. 得到sqlsessionfatory工厂模式,
  3. 由此创建sqlsession对象,
  4. 产生对应接口的SQLMapper对象,
  5. 执行数据库操作,最后结束.

从 XML 中构建 SqlSessionFactory

  1. SqlSessionFactoryBuilder 可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例
  2. 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。

从 SqlSessionFactory 中获取 SqlSession

  1. 我们可以从SqlSessionFactory中获得 SqlSession 的实例

  2. SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。

通过 SqlSession 实例来直接执行已映射的 SQL 语句

  1. sqlsession执行rselectOne()或者selectList()等方法,直接执行sql操作,最后结束

  2. 这种方式对使用旧版本 MyBatis 的用户来说,比较熟悉。

更简洁的方式

  1. 使用指定了语句的参数和返回值相匹配的接口(比如 BlogMapper.class)

  2. 现在代码更清晰,更加类型安全不用担心可能出错的字符串字面值,以及强制类型转换

BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);

作用域

MyBatis基础原理_第2张图片


MyBatis基础原理_第3张图片


结果集映射:解决数据库字段与对象属性不对应的问题

作用就是:把数据库中返回的字段的值,映射到对象的属性上

对象中有id和age两个属性

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

  private int id;
  private String ame;

}

配置结果集映射


<resultMap id="tea" type="teacher">
  <id column="id" property="id"/>
  
  <result column="name" property="ame"/>
resultMap>


<select id="getTeacherByName" resultMap="tea" parameterType="teacher">
  select * from teacher where id = #{id} and name = #{ame};
select>

执行查询

Teacher teacher1 = new Teacher(2, "李长歌");
Teacher teacher = mapper.getTeacherByName(teacher1);

写resultMap时,resultType就可以不用写了


日志工厂

Mybatis 通过使用内置的日志工厂提供日志功能。内置日志工厂将会把日志工作委托给下面的实现之一:

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging
  1. MyBatis 内置日志工厂:会基于运行时检测信息,选择日志委托实现

  2. 它会(按上面罗列的顺序)使用第一个查找到的实现

  3. 如果你的环境中并不存在 Log4J,你却试图调用了相应的方法,MyBatis 就会忽略这一切换请求 .

  4. 没有找到这些实现时,将会禁用日志功能


STDOUT_LOGGING(标准输出记录)

xml声明

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

执行结果

//读者入口
Reader entry: <?xml version="1.0" encoding="UTF-8" ?>
//检查类 com.changGe.li.mapper.StudentMapper 是否符合条件 [可分配给对象]  
Checking to see if class com.changGe.li.mapper.StudentMapper matches criteria [is assignable to Object]
  
Checking to see if class com.changGe.li.mapper.TeacherMapper matches criteria [is assignable to Object]
  
//打开 JDBC 连接  
Opening JDBC Connection
//创建连接 1312381159  
Created connection 1312381159.
//在 JDBC 连接 [com.mysql.cj.jdbc.ConnectionImpl@4e3958e7] 上将 autocommit(自动提交) 设置为 false  
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4e3958e7]
//准备中  
==>  Preparing: select * from teacher where id = ? and name = ?;
//参数
==> Parameters: 2(Integer), 李长歌(String)
//列  
<==    Columns: id, name
//行  
<==        Row: 2, 李长歌
//总计  
<==      Total: 1
//返回结果  
Teacher(id=2, name=李长歌)
//在 JDBC 连接 [com.mysql.cj.jdbc.ConnectionImpl@4e3958e7] 上将自动提交重置为 true  
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4e3958e7]
//关闭 JDBC 连接 [com.mysql.cj.jdbc.ConnectionImpl@4e3958e7]  
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4e3958e7]
//将连接 1312381159 返回到池  
Returned connection 1312381159 to pool.

Log4j :java日志

log4j_百度百科 (baidu.com)

使用Log4j框架的作用通俗的解释:

  • 能够控制日志信息想往哪里打就往哪里打,比如:控制台、文件、邮箱、数据库等等。
  • 能够控制日志信息想怎么打就怎么打,比如:我想要打印时间、程序的名称、程序的方法名、程序的行号、线程的名称等等。
  • 能够控制日志信息想打什么打什么,不想打的就不打,日志信息是分级别的,有时候我只想看错误的信息或者警告的信息,有时候我想看到所有的信息我想调试程序等等。

依赖

<dependency>
  <groupId>log4jgroupId>
  <artifactId>log4jartifactId>
  <version>1.2.17version>
dependency>

log4j.properties

#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
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=【%c】-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n

#日志输出级别:输出DEBUG级别及以上的日志信息
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG  

简单使用

public class MyBatisTest {

  //在当前类运行时,输出日志
  private static Logger logger = Logger.getLogger(MyBatisTest.class);


  @Test
  public void test(){

    //在debug时,输出当前为debug模式
    logger.debug("当前为debug模式");

    SqlSession sqlSession = MyBatisUtil.getSqlSession();

    //输出一条普通信息:打印sqlSession
    logger.info(sqlSession);

    TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

    Teacher teacher1 = new Teacher(2, "李长歌");
    Teacher teacher = mapper.getTeacherByName(teacher1);

    System.out.println(teacher);

    sqlSession.close();

    try {}catch (Exception e){}
    finally {
      logger.error("报错了");
    }

  }

}

日志文件的存放地址

部分日志信息

MyBatis基础原理_第4张图片


分页

RowBounds

<select id="getStudent" resultType="student">
  select * from student;
select>
@Test
public void test(){

  SqlSession sqlSession = MyBatisUtil.getSqlSession();

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

  //分页,从3开始显示,显示4个
  RowBounds rowBounds = new RowBounds(2, 4);

  //方法路径,参数和分页
  List<Student> students = sqlSession.selectList("com.changGe.li.mapper.StudentMapper.getStudent", null, rowBounds);


  for (com.changGe.li.pojo.Student student : students) {
    System.out.println(student);
  }

  sqlSession.close();

}

MyBatis的分页插件:PageHelper: MyBatis 分页插件 PageHelper


注解开发

根本目的为了解耦

@Select("select * from student;")
List<Student> getStudent();
  1. 运行的本质是:sqlSession(User.class)获取User的类对象,
  2. 然后获取其中所有的数据,读取特定数据上的注解,
  3. 得到其中的value(就是我们写的sql语句),
  4. 自动帮我们配置环境,运行.

Mapper中有一个sqlSession,里面保存了所有的配置信息

MyBatis基础原理_第5张图片


增删改

TeacherMapper

@Insert("insert into teacher values(#{id},#{name});")
/**
   * 将传参id的值交给@Param中的id,然后赋值给sql语句中的#{id}
   *
   * @Param中的参数名必须与#{}一致,mybatis就是从这里取值的
   */
int insertTeacher(@Param("id")int id,@Param("name")String name);
//设置开启自动提交
return sqlSessionFactory.openSession(true);

注解里的需要的参数,会自动去方法传参中找.

比如传参是对象,就会去自动匹配对象的字段。

@Insert("insert into teacher values(#{id},#{name});")
/**
   * 注解里的需要的参数,会自动去方法传参中找.
   */
int insertTeacher(Teacher teacher);
mapper.insertTeacher(new Teacher(4,"太平公主"));

运行分析

MyBatis运行流程(源码):https://zhuanlan.zhihu.com/p/67738448

简略易读版本: MyBatis的执行流程详解 - 知乎 (zhihu.com)

  1. Resource类读取配置文件,创建SqlSessionFactroyBuilder对象

    //读取配置文件的流
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    
    //sql会话工厂构建类 构建 得到sql会话工厂
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    
  2. 这个对象底层用,创建了一个XMLConfig对象.

    public SqlSessionFactory build(InputStream inputStream) {
      return build(inputStream, null, null);
    }
    
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
      try {
        //通过流读取properties文件,创建XMLConfigBUiler对象
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        
        //XMLConfig对象读取xml配置文件
        //解析文件中数据,返回SqlSessionFactory对象
        return build(parser.parse());
        
      } catch (Exception e) {} finally {
        
        ErrorContext.instance().reset();
        
      }
      
    }
    
  3. XMLConfigBUiler对象读取xml配置文件,创建Configuration对象,这个对象中包含了所有的基本配置信息

    sqlSession中包含了configuration,里面有所有的配置信息,如environment(环境)

MyBatis基础原理_第6张图片

public Configuration parse() {
  
  if (parsed) {
    
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  
  return configuration;
}
  1. SqlSessionFactroy工厂对象,构建SqlSession对象(openSession)时,底层创建Transactional事务管理器–监听事务.同时创建Execetor执行器

    sqlSession中包含executor和transaction对象

MyBatis基础原理_第7张图片

return sqlSessionFactory.openSession();

SqlSessionFactory的实现类:DefaultSqlSessionFactory

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
  
  //新建事务管理器
  Transaction tx = null;
  
  try {
    
    //获取环境
    final Environment environment = configuration.getEnvironment();
    
    //从环境中获取:事务管理器工厂
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
    
    //事务管理器赋值:所有的环境数据,等级和是否自动提交
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
    
    //创建执行器,专门用来执行sql操作,和缓存等
    final Executor executor = configuration.newExecutor(tx, execType);
    
    //把事务管理器和执行器都返回
    return new DefaultSqlSession(configuration, executor, autoCommit);
    
  } catch (Exception e) {
    //关闭事务管理器
    closeTransaction(tx); // may have fetched a connection so lets call close()
    
    throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
  }
  
}

lombok

它是一个插件,还需要maven依赖(也可以直接引用依赖)

<dependency>
  <groupId>org.projectlombokgroupId>
  <artifactId>lombokartifactId>
  <version>1.18.22version>
dependency>

@Data @AllArgsConstractor @NoArgsConstractor

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

有一种说法是lombok改变了java的源码,让java语言出现了"文明断层"的情况

你可能感兴趣的:(java基础,JavaWeb,java,后端,java-ee,架构)