JavaLearn#(26)MyBatis基础:认识框架MyBatis环境搭建基本CRUD配置文件日志管理别名属性文件ThreadLocal保存sqlSession本地DTD模板

Posted LRcoding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaLearn#(26)MyBatis基础:认识框架MyBatis环境搭建基本CRUD配置文件日志管理别名属性文件ThreadLocal保存sqlSession本地DTD模板相关的知识,希望对你有一定的参考价值。

1. MyBatis入门

是一个基于 Java 的持久层框架,将接口和 Java的 POJOs(Plain Ordinary Java Object,普通的Java对象)映射成数据库中的记录 【ORM】

1.1 认识框架

框架(Framework)是一个框子—约束性,也是一个架子—支撑性。框架类似于一个毛坯房,由开发人员在毛坯房基础上进行装修

使用框架可以减少开发时间、降低开发难度、保证设计质量,还可以起到约束,可以降低程序员之间沟通和日后维护的成本

1.2 ORM

JDBC缺点:需要手动的完成面向对象的Java语言和面向关系的数据库之间数据的转换(得到了一个结果集,将数据挨着取出来封装到一个对象中)

ORM(Object-Relational Mapping)对象关系映射:在关系型数据库对象之间做一个映射,就可以像操作对象一样操作数据库了

持久化:将内存中的数据保存到关系型数据库

持久层:专注于实现数据持久化应用领域(内存到数据库,数据库到内存等)的某个特定系统的一个逻辑层面

1.3 认识 MyBatis

MyBatis 是一个半自动 ORM框架(SQL语句由用户书写),其本质是对 JDBC的封装。使用 MyBatis 重点需要程序员编写 SQL,不需要写JDBC代码

Hibernate 是一个全自动的ORM框架(SQL语句、解析、执行全由Hibernate完成),不需要手写SQL,但是缺少了灵活性,SQL的调优也比较麻烦

2. MyBatis快速上手

2.1 搭建项目环境

项目总体框架

  • 引入 jar包(此处手动导入[从MyBatis官网下载]、后期使用maven自动导入即可)

  • 准备配置文件 (mybatis-cfg.xml 官方源码配置文件的名字)----- 位置在 src 包下

    <?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>
        <!-- 配置参数(log4j使用) -->
        
        <!-- 环境参数配置 -->
        <environments default="mysql">
            <environment id="mysql">
                <!-- 配置事务管理器 -->
                <transactionManager type="JDBC"></transactionManager>
                <!-- 数据源:连接池类型的 -->
                <dataSource type="POOLED">
                    <!-- name的值相当于是固定的 -->
                    <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                    <property name="url" value="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/>
                    <property name="username" value="root"/>
                    <property name="password" value="root"/>
                </dataSource>
            </environment>
        </environments>
        
      	<!-- 告诉 MyBatis 映射文件的位置(映射文件使用) -->
    </configuration>
    

    注意

    • XML文件需要提供dtd或者xsd文件,来定义XML文件的标签结构
    • 数据库四个连接参数的name属性的值来自org.apache.ibatis.datasource.unpooled包(mybatis的类包)下类UnpooledDataSource
  • 准备映射文件

    <?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">
    <!-- 每个【映射文件】都有自己的命名空间,此时还未进行接口绑定,名称任意 -->
    <mapper namespace="com.lwclick.mapper.EmpMapper">
    
    </mapper>
    
  • 配置文件 mybatis-cfg.xml 中,指定映射文件的位置

    <!-- 告诉 MyBatis 映射文件的位置 -->
    <mappers>
        <mapper resource="com/lwclick/mapper/EmpMapper.xml"></mapper>
    </mappers>
    
  • 引入 log4j 进行功能测试(请使用最新版,避免漏洞)

    • 配置文件中,加入配置

      <!-- 配置参数 -->
      <settings>
          <!-- 指定所使用的日志框架 -->
          <setting name="logImpl" value="LOG4J"/>
      </settings>
      
    • 创建 log4j 的配置文件 ----- 位置在 src 包下

      # 定义日志的全局级别
      log4j.rootLogger=error,stdout,logfile
      
      # 设置日志的局部级别
      # 如果访问该包下的资源,日志级别精确到 debug级
      log4j.logger.com.lwclick.mapper=debug
          # 接口级别日志
          # log4j.logger.com.lwclick.mapper.EmpMapper=debug
          # 方法级别日志
          # log4j.logger.com.lwclick.mapper.EmpMapper.findBy
      
      log4j.appender.stdout=org.apache.log4j.ConsoleAppender
      log4j.appender.stdout.Target=System.err
      log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
      
      log4j.appender.logfile=org.apache.log4j.FileAppender
      log4j.appender.logfile.File=d:/sys.log
      log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
      log4j.appender.logfile.layout.ConversionPattern=%dyyyy-MM-dd HH:mm:ss %l %F %p %m%n
      

2.2 完成 select操作

首先需要先创建实体类 Employee:

public class Employee 
    private int empno;
    private String ename;
    private String job;
    private Date hireDate;
    private double sal;
	// getter / setter / toString() / 有参,无参构造方法

环境搭建完成后,完成 select操作,只涉及到映射文件测试类

2.2.1 查询所有操作

映射文件

<!-- 返回值如果是 List,resultType 要写集合的元素的类型,也就是泛型类型 -->
<select id="selectAll" resultType="com.lwclick.entity.Employee">
    SELECT * FROM emp
</select>

测试类(使用 JUnit 测试框架):

public class TestEmployee 
    // JUnit 的 @Test注解
    @Test
    public void testSelectAll() throws IOException 
        // 1. 创建 SqlSessionFactory
        InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

        // 2. 使用 SqlSessionFactory创建一个 SqlSession
        SqlSession sqlSession = factory.openSession();

        // 3. 完成数据库操作并得到结果
        List<Employee> list = sqlSession.selectList("com.lwclick.mapper.EmpMapper.selectAll");  // 映射文件的 namespace名 + SQL语句的 id

        // 4. 关闭资源
        sqlSession.close();

        // 5. 输出结果
        list.forEach(System.out::println);
    

常见异常

  • 配置文件中的mappers中没有指定映射文件的路径或者namespace+id写错了
    • Cause: java.lang.IllegalArgumentException:Mapped Statements collection does not contain value for com.lwclick.mapper.EmpMapper.selectAll
  • 缺少了ResultType或者ResultMap的指定
    • Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement ‘com.lwclick.mapper.EmpMapper.selectAll’. It’s likely that neither a Result Type nor a Result Map was specified.

2.2.2 通过id查询

映射文件

<select id="selectById" parameterType="int" resultType="com.lwclick.entity.Employee">
    SELECT * FROM emp WHERE empno = #param1
</select>

测试类

@Test
public void testSelectById() throws IOException 
    InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

    SqlSession sqlSession = factory.openSession();

    // 7839为参数
    Employee employee = sqlSession.selectOne("com.lwclick.mapper.EmpMapper.selectById", 7839);

    sqlSession.close();

    System.out.println(employee);

2.2.3 通过参数查询

映射文件

<!-- 如果参数是一个 JavaBean,会根据参数拼接 【getter方法】 并调用 -->
<select id="selectEmpByParams" parameterType="com.lwclick.entity.Employee" resultType="com.lwclick.entity.Employee">
    SELECT * FROM emp WHERE job = #job AND sal >= #sal
</select>

测试类

@Test
public void testSelectEmpByParams() throws IOException 
    InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

    SqlSession sqlSession = factory.openSession();

    Employee emp = new Employee();
    emp.setJob("CLERK");
    emp.setSal(3000);
    List<Employee> list = sqlSession.selectList("com.lwclick.mapper.EmpMapper.selectEmpByParams", emp);

    sqlSession.close();

    list.forEach((e) -> 
        System.out.println(e);
    );

2.2.4 总结

映射文件中细节:

  • 每个SQL语句的 id必须唯一

  • parameterType(参数类型),可以省略,MyBatis会自动推断类型; 如果提供,类型必须正确

    取值只能是一个类型,所以后续都省略此参数

  • resultType(返回值类型)

    • 返回值是集合,取值为集合的元素泛型类型完整路径名
    • 返回值是对象,取值为对应类完整路径名
  • 方法参数的传递

    • 参数是基本类型(目前sqlSession这种方式),使用 #param1
    • 参数是引用数据类型,使用 #属性名 接收数据,底层调用的是其getter方法如果没有getter方法,就会直接找同名属性

自动映射(Auto Mapping):

  • 按照名称自动把结果集中的数据映射到对象属性中(如果某个属性没有 setter方法,那就直接找同名的属性
  • 如果名称不一样,最简单的是MySQL别名的方式, select sal as salary,保证与实体类中的属性名称一致

2.3 完成DML操作

2.3.1 insert 操作

注意事项

  • DML操作的底层调用executeUpdate(),返回值都是int类型,无需指定
  • 执行DML操作,默认事务手动提交,此时有两种解决方式
    • 使用自动提交
    • 手动执行 commit() 或者 rollback() 结束事务推荐

映射文件

<!-- DML操作,底层调用 executeUpdate(),返回值都是 int,无需指定 -->
<insert id="saveEmp">
    INSERT INTO emp VALUES (null, #ename, #job, #hireDate, #sal)
</insert>

测试类

@Test
public void testSaveEmp() throws IOException 
    InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

    // 自动事务提交   SqlSession sqlSession = factory.openSession(true);
    SqlSession sqlSession = factory.openSession();

    Employee emp = new Employee("zhangsan", "clerk", Date.valueOf("1999-12-03"), 3000);
    int n = sqlSession.insert("com.lwclick.mapper.EmpMapper.saveEmp", emp);
    
    // 手动提交事务!!!!!
    sqlSession.commit();

    sqlSession.close();

    System.out.println(n);

2.3.2 update操作

映射文件

<update id="updateEmp">
    UPDATE emp SET job = #job, sal = #sal WHERE empno = #empno
</update>

测试类

@Test
public void testUpdateEmp() throws IOException 
    InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

    SqlSession sqlSession = factory.openSession();

    Employee emp = new Employee();
    emp.setJob("clerk");
    emp.setSal(5000);
    emp.setEmpno(9839);
    int n = sqlSession.update("com.lwclick.mapper.EmpMapper.updateEmp", emp);

    // 手动提交事务
    sqlSession.commit();

    sqlSession.close();

    System.out.println(n);

2.3.3 delete操作

映射文件

<delete id="deleteEmp">
    DELETE FROM emp WHERE empno = #param1
</delete>

测试类

@Test
public void testDeleteEmp() throws IOException 
    InputStream is = Resources.getResourceAsStream("mybatis-cfg.xml");
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

    SqlSession sqlSession = factory.openSession();

    int n = sqlSession.delete("com.lwclick.mapper.EmpMapper.deleteEmp", 7839);

    // 手动提交事务
    sqlSession.commit();

    sqlSession.close();

    System.out.println(n);

2.3.4 总结

其实一个SqlSession的update、delete、insert中任意一个方法均可完成所有DML操作。底层都是调用的update方法,就好比JDBC中的executeUpdate()可以完成DML操作一样。

3. MyBatis功能详解

3.1 关于 jar包

编号元素列表作用
1mybatis-3.5.2.jarMybatis核心jar包(包含全部的功能)
2ant-1.10.3.jar; ant-launcher-1.10.3.jar将软件编译、测试、部署等步骤联系在一起加以自动化的一个软件构建工具
3asm-7.0.jar代码生成,代码混淆,代码转换等等以字节码为操作目标的工作,一定程度上类似javac的功能
4cglib-3.2.10.jar实现动态代理的技术,延迟加载时使用
5javassist-3.24.1-GA.jar可用来检查、”动态”修改及创建 Java类。功能与JDK自带反射功能类似,但比反射功能更强大
6ognl-3.2.10.jar对象导航图语言的缩写,功能强大的表达式语言工具包。在动态SQL和$param中使用
7commons-logging-1.2.jar日志包
8slf4j-api-1.7.26.jar日志包
9slf4j-log4j12-1.7.26.jar日志包
10log4j-1.2.17.jar日志包
11log4j-api-2.11.2.jar日志包
12log4j-core-2.11.2.jar日志包

3.2 核心 API

  • SqlSessionFactoryBuilder: 根据配置或者代码生成SqlSessionFactory(将各个工厂生产的东西组装起来),采用的是分步构建的构建者模式
    • 当SqlSessionFactory创建后,该对象就没有存在的必要了
  • SqlSessionFactory:生产SqlSession,使用的是工厂模式
    • 在应用执行期间一直存在,需要一直生产SqlSession
  • SqlSession:可获取Mapper的接口,也可以发送SQL语句并返回结果(相当于Connection)
    • 每个线程都有自己的 SqlSession实例,是线程不安全的,不能被共享
  • Mapper:由一个Java接口XML文件(或者注解)构成,需要给出对应的SQL和映射规则,负责发送SQL去执行并返回结果
    • 关闭了 SqlSession也就关闭了由其所产生的Mapper

3.3 关于配置文件

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

    <environments default="mysql">
        <environment id="mysql">
            <!-- 配置事务管理器 -->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 数据源:连接池类型的 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 告诉 MyBatis 映射文件的位置 -->
    <mappers>
        <mapper resource="com/lwclick/mapper/EmpMapper.xml"></mapper>
    </mappers>
</configuration>

mybatis-cfg.xml 配置文件中,配置项的顺序是有规定的,在 dtd文件中进行了规定

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

environment配置项中的配置参数

  • transactionManager(事务管理器,有两种取值 JDBC | MANAGED)

    • JDBC:简单使用了 JDBC 的提交和回滚设置。它依赖于从数据源得到的连接来管理事务范围。
    • MANAGED:几乎没做什么,从来不提交或回滚一个连接。会让容器(如Spring)来管理事务的整个生命周期

  • dataSource(数据源,有三种取值 UNPOOLED | POOLED | JNDI)

    • UNPOOLED:每次请求都要打开连接,不用就关闭,未采用连接池

      • driver、url、username、password
      • defaultTransactionIsolationLevel(默认的连接事务隔离级别)

      注意:由上图可知,property 的name取值,必须是图中的属性名

    • POOLED:连接池方式(目前就采用这种)

      • poolMaximumActiveConnections:在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
      • poolMaximumIdleConnections:任意时间可能存在的空闲连接数
      • 其他一些参数。。。

    • JNDI:使用 Spring这类容器时,容器可以集中或在外部配置数据源

mappers配置项的配置

  • mapper:告诉MyBatis 去哪找映射文件
    • resource:资源文件的地址,使用 / 不是用 . ( resource=“com/lwclick/mapper/EmpMapper.xml” )
    • url:全限定路径,资源文件在本地的绝对路径,以file开始
    • class:接口的全限定地址,使用 . 不是用 / (class=“com.lwclick.mapper.EmpMapper”) — EmpMapper为接口
  • package:包下的所有接口,不用再一个个的指定(必须是接口
    • name:包的名字( name=“com.lwclick.mapper” )

3.4 关于日志管理

MyBatis 的内置日志工厂(LogFactory)提供日志功能,内置日志工厂将日志交给以下某一个工具做代理(顺序查找)

  • SLF4J
  • Apache Commons Logging
  • Log4j 2
  • Log4j
  • JDK logging
  • NO_LOGGING

在项目中把日志工具环境配置好之后,不用在MyBatis进行配置也可以让日志生效,最好还是使用 setting进行配置(防止使用默认的日志框架)

<settings>
    <!-- 指定所使用的日志框架 -->
    <setting name="logImpl" value="LOG4J"/>
</settings>

其中 setting 的name的取值是固定的

4. 功能的完善

4.1 定义别名

4.1.1 单个文件配置

mybatis-cfg.xml 配置文件中,使用 typeAliases 定义别名

<!-- 定义类型的别名 -->
<typeAliases>
    <typeAlias type="com.lwclick.entity.Employee" alias="employee"></typeAlias>
</typeAliases>

那么在映射文件的 resultType中,就可以直接使用 employee了

<sele

以上是关于JavaLearn#(26)MyBatis基础:认识框架MyBatis环境搭建基本CRUD配置文件日志管理别名属性文件ThreadLocal保存sqlSession本地DTD模板的主要内容,如果未能解决你的问题,请参考以下文章

JavaLearn#(27)MyBatis进阶:Mapper代理(接口绑定)多参数传递模糊查询分页自增主键回填动态SQL一级缓存二级缓存

JavaLearn#(27)MyBatis进阶:Mapper代理(接口绑定)多参数传递模糊查询分页自增主键回填动态SQL一级缓存二级缓存

JavaLearn#(28)MyBatis高级:无级联查询级联查询(立即加载结果映射延迟加载)多表连接查询MyBatis注解MyBatis运行原理面试题

JavaLearn#(20)注解元注解模拟MyBatis注解JDK新特性数据库建模UML建模

JavaLearn#(20)注解元注解模拟MyBatis注解JDK新特性数据库建模UML建模

org.mybatis总是认不出来的原因,pom.xml中有