01Mybatis总结

Mybastis总结

1.什么是mybatis?为什么使用mybatis?

​ mybatis的前身是ibatis。MyBatis 是一个可以自定义SQL、存储过程和高级映射的持久层框架。MyBatis 摒除了大部分的JDBC代码、手工设置参数和结果集重获。MyBatis 只使用简单的XML 和注解来配置和映射基本数据类型、Map 接口和POJO到数据库记录。相对Hibernate和Apache OJB等“一站式”ORM解决方案而言,Mybatis 是一种“半自动化”的ORM实现。通俗的说,mybatis是用来简化Java后台和数据库之间交互的工具。具体简化内容见下文。文末讲解----使用jdbc开发时,和mybatis相比的不足。---- ----mybatis和hibernate的区别。----

​ mybatis框架,是一个持久层框架,是apache下的*项目。mybatis让程序员将主要精力放在sql上,通过mytabis提供的映射方式,自动生成满足需要的sql语句mybatis可以向PreparedStatement中输入参数自动进行输入映射,将查询结果集灵活的映射成Java对象(输出映射),输入映射和输出映射这是mybatis的核心 。

​ ==为什么使用mybatis?==MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

2.mybatis框架执行流程图

01Mybatis总结

1.执行流程

1.Mybatis 读取config.xml配置文件(mybatis全局配置文件)后会将内容放在一个Configuration类中。

2.SqlSessionFactoryBuilder会读取Configuration类中信息创建SqlSessionFactory。

3.SqlSessionFactory根据需求创建SqlSession(类似connection)(每次访问数据库都需要一个SqlSession)

4.底层(不需要程序员管理)封装对象,向数据库输入参数。

5.使用sqlsession直接或间接(mapper)操作(Update、Select、Insert、Delete)数据库

6.底层使用executor执行器执行操作数据库。

7.底层封装对象,数据库向后台返回所需求的值,也就是针对于数据库来说是输出结果(值或对象)。

2.对于执行流程的详细琐碎解释:

1.Mybatis 读取mybatis-config.xml配置文件(mybatis全局配置文件)后会将内容放在一个Configuration类中,Configuration类会存在整个Mybatis生命周期,以便重复读取。SqlSessionFactoryBuilder会读取Configuration类中信息创建SqlSessionFactory。

2.Mybatis中SqlSessionFactiory、SqlSession等都为接口,Mybatis默认使用的实现类为DefaultSqlSessionFactory和DefaultSqlSession类。

3.SqlSession用途主要有两种

①. 获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果。

②. 直接使用SqlSession,通过命名信息去执行SQL返回结果,该方式是IBatis版本留下的,SqlSession通过Update、Select、Insert、Delete等方法操作。

针对Mapper方式,Mybatis底层利用JDK动态代理技术实现该接口,底层最后还是使用的IBatis中SqlSession通过Update、Select、Insert、Delete等方法操作。

4.关于对象的生命周期:

①. SqlSessionFactoryBuilder的作用是创建SqlSessionFactiory,在创建完成后就会被回收,所以它的生命周期只存在于局部方法。

②.SqlSessionFactiory是单例的,作用就是创建SqlSession,每次访问数据库都需要一个SqlSession,所以SqlSessionFactiory的生命周期是贯穿整个Mybatis生命周期的,SqlSessionFactiory采用单例的原因是减少数据库连接资源。

③. SqlSession是一个会话,类似于jdbc的connection,它的生命周期是在请求数据库处理事务过程中,他是一个线程不安全的对象。多线程操作时因多注意他的隔离级别和数据库锁等。SqlSession使用需及时关闭。

④. Mapper是一个接口,它的作用是发送SQL,返回结果,因此他的生命周期不会大于SqlSession。

5.关于SqlSessionFactory的创建,Mybatis采用构造模式来完成创建。

第一步:XMLConfigBuilder解析XML配置,读出配置参数,存入Configuration类中。(底层实现)

第二步:SqlSessionFactoryBuilder会读取Configuration类中的信息创建SqlSessionFactory。

inputStream = Resources.getResourceAsStream(“mybatis-config.xml”);

sqlSessionFactory = new SqlSessionFactoryBuilder.builder(inputStream)inputStream为读取配置文件的流;

6.映射器由MappedStatement、SqlSource、BoundSql三部分组成。

①. MappedStatement,它保存着映射器的一个节点(select|update|insert|delete)。包括许多我们配置的SQL、SQL的ID、缓存信息、resultMap、parameterType、resultType等配置内容。

②. SqlSource,它提供BoundSql对象。是MappedStatement的一个属性。、

③. BoundSql,它是建立SQL和参数的地方,常用的三个属性:SQL、parameterObject、parameterMappings;

parameterObject就是我们传入的参数;

parameterMappings实则是List,ParameterMapping描述了我们的参数,包括属性、名称、表达式、javaType、jdbcType、typeHandler等重要信息,通过它实现参数和SQL的结合,以便PreparedStatement能够通过它找到parameterObject对象的属性并设置参数。

3.代码Mybatis工具类

package com.sz.util;
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{
        String resource = "mybatis.cfg.xml";
        InputStream in = null;
        try {
            in = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    //返回Sqlsession,用其来直接增删查改数据库或者间接通过mapper来操作。
    public static SqlSession getSession(){
        return sqlSessionFactory.openSession();
    }
}

3.具体代码展示,快速入门。简单例子。

1.首先导入关于mybatis的jar包

​ 我这里用的是maven,所以直接在pom.xml中添加依赖即可。因为mybatis是简化后台与数据库交互的,所以我这里添加了MySQL的依赖和log4J日志依赖,方便查看操作。

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.38</version>
</dependency>
  <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
  <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
  </dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
  <groupId>org.mybatis.generator</groupId>
  <artifactId>mybatis-generator-core</artifactId>
  <version>1.3.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.17</version>
</dependency>

2.后台与数据库交互,那必然得有数据库连接的信息了,所以得有jdbc.properties

url=jdbc:mysql://localhost:3306/mybatis3
username=root
password=123456
driver=com.mysql.jdbc.Driver

3.我这里用到了log4j日志依赖,方便查操作,所以增加了log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.com.sz.mapper=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

4.全局配置mybatis.cfg.xml,具体标签含义后面统一总结

<?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>
    
    <properties resource="jdbc.properties">
    </properties>
    
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="UNPOOLED">
                <property name="url" value="${url}"/>
                <property name="driver" value="${driver}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <mappers>
    	<package name="com.sz.mapper"/>
    </mappers>
</configuration>

5.创建一个MybatisUtil.class类,用于获取sqlsession对象

package com.sz.util;
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{
        String resource = "mybatis.cfg.xml";
        InputStream in = null;
        try {
             in = Resources.getResourceAsStream(resource); 
             sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static SqlSession getSession(){ 
        return sqlSessionFactory.openSession();
    }
}

6.创建一个简单的实例Girl.class

package com.sz.pojo;
import java.util.Date;
public class Girl {
    private Long id;
    private String lastName;
    private String flower;
    private Date birthday;
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getFlower() {
        return flower;
    }
    public void setFlower(String flower) {
        this.flower = flower;
    }
    public Date getBirthday() {
        return birthday;
    }
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

7.创建对应的GirlMapper.class

package com.sz.mapper;
import com.sz.pojo.Girl;
import org.apache.ibatis.annotations.Param;
import java.util.Map;

public interface GirlMapper {
    int insert(Girl girl);
    Girl queryById(Long id);   
    Girl queryById2(long id);    
    Girl queryByName(String name);
    Girl queryByNameFlower(@Param("name") String name, @Param("flower") String flower); 
    Girl queryByNameFlower2(Girl girl);    
    Girl queryByNameFlower3(Map<String,Object> map); 
}

8.创建与GirlMapper.class对应的GirlMapper.xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN" "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">
<mapper namespace="com.sz.mapper.GirlMapper">
    
    <insert id = "insert">
        insert into girl (name,flower,birthday) values (#{name},#{flower},#{birthday})
    </insert>
    
    <select id="queryById" resultType="com.sz.pojo.Girl">
        select * from girl where id = #{id}
    </select>

    <select id="queryById2" resultType="com.sz.pojo.Girl">
        select * from girl where id = #{id}
    </select>
    
    <select id ="queryByName" resultType="com.sz.pojo.Girl">
        select * from girl where last_name = #{name}
    </select>
    
    <!--使用默认的参数名称风格,非常不友好,这种方式是断然不可取的。-->
    <select id ="queryByNameFlower" resultType="com.sz.pojo.Girl">
        <!--select * from girl where last_name = #{param1} and flower = #{param2}-->
        <!--select * from girl where last_name = #{arg0} and flower = #{arg1}-->
        select * from girl where last_name = #{name} and flower = #{flower}
    </select>

    <select id ="queryByNameFlower2" resultType="com.sz.pojo.Girl">
        select * from girl where last_name = #{lastName} and flower = #{flower}
    </select>

    <select id ="queryByNameFlower3" resultType="com.sz.pojo.Girl">
        select * from girl where last_name = #{lastName} and flower = #{flower}
    </select>
    
    <select id="queryByAB" resultType="com.sz.pojo.Girl">
        <!--select * from girl where last_name = #{arg0.name} and flower = #{arg1.flower}-->
        select * from girl where last_name = #{a.name} and flower = #{b.flower}
    </select>
</mapper>

9.测试类

@Test
public void m2(){
	SqlSession sqlSession = MybatisUtil.getSession();
	GirlMapper mapper = sqlSession.getMapper(GirlMapper.class);
	Girl girl = mapper.queryById(1L);
	System.out.println(girl);
	System.out.println(girl.getLastName());
	sqlSession.close();
}

4.传统JDBC操作数据库

1.创建连接,2.操作数据

package com.jdbc.example;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JdbcExample {

    //创建一个数据库的连接
    private static Connection getConnection() {
        Connection connection = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");//加载用户驱动
            String url = "jdbc:mysql://localhost:3306/mybatis";//连接数据库的地址
            String user = "root";//数据库的用户名
            String password = "root";//数据库的密码
            connection = DriverManager.getConnection(url, user, password);//得到一个数据库的连接
        } catch (ClassNotFoundException e) {
            // TODO 自动生成的 catch 块
            System.out.println(JdbcExample.class.getName() + "数据库驱动包未找到!");
            return null;
        } catch (SQLException e) {
            // TODO 自动生成的 catch 块
            System.out.println(JdbcExample.class.getName() + "SQL语句有问题,无法查询成功!");
            return null;
        }
        return connection;//返回该连接
    }


    public User getUser(int id) {
        Connection connection = getConnection();//得到该数据库的连接
        PreparedStatement ps = null;//声明一个null的预处理的Statement
        ResultSet rs = null;//声明一个结果集,用来存放SQL的查询后的结果
        try {
            ps = connection.prepareStatement("select * from user where id=?");//对查询的User表的SQL进行预处理编译
            ps.setInt(1, id);//把参数Id设值到数据的条件中
            rs = ps.executeQuery();//执行查询语句。把结果返回到ResultSet结果集中
            while (rs.next()) {//遍历从结果集中取数
                int user_id = rs.getInt("id");//取出Statement的用户id
                String username = rs.getString("username");//取出Statement的用户名
                Date birthday = rs.getDate("birthday");//取出Statement的生日
                String sex = rs.getString("sex");//取出Statement的性别
                String address = rs.getString("address");//取出Statement的用户地址

                User user = new User();//创建一个User类的实体对象POJO
                user.setId(user_id);//存放在user对象中
                user.setUsername(username);
                user.setBirthday(birthday);
                user.setSex(sex);
                user.setAddress(address);

                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            this.close(rs, ps, connection);
        }
        return null;
    }

    //判断数据库是否关闭
    /***
     * 
     * @param rs 查看结果集是滞关闭
     * @param stmt 预处理SQL是否关闭
     * @param conn 数据库连接是否关闭
     */
    private void close(ResultSet rs, Statement stmt, Connection conn) {

        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            System.out.println(JdbcExample.class.getName() + "ResultSet 关闭失败!");
        }
        try {
            if (stmt != null) {
                stmt.close();
            }
        } catch (SQLException e) {
            System.out.println(JdbcExample.class.getName() + "Statement 关闭失败!");
        }
        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            System.out.println(JdbcExample.class.getName() + "Connection 关闭失败!");
        }
    }
    public static void main(String[] args) {
        User user = new JdbcExample().getUser(1);//我们查询用户的id 为 1 用户 
        System.out.println(user);//打印输出查询出来的数据

    }

}

5.JDBC与mybatis比较

1,数据库连接,使用时就创建,不使用就释放,对数据库进行频繁连接开关和关闭,造成数据库资源浪费,影响数据库的性能
解决:使用数据库连接池管理数据库的连接
2,sql语句使用硬编码在java程序中,修改sql语句,就需要重新编译java代码,不利于系统维护
解决:把sql语句放在xml配置文件中,修改sql语句也不需要重新编译java代码
3,向预编译语句PreparedStatement中设置参数,对占位符位置和设置参数值,硬编码,修改sql语句也不需要重新编译java代码
解决:把sql语句和占位符设置参数值放在xml配置文件中
4,从result中遍历结果集数据时,存在硬编码,将获取表的字段进行硬编码。

解决:将查询的结果集,自动映射成 java对象。

6.Mybatis与Hibernate比较

https://blog.csdn.net/fupengyao/article/details/51538004

7.详解标签

详细见官方文档http://www.mybatis.org/mybatis-3/zh/configuration.html。核心是下面的1,2,3.

1.全局xml配置标签

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

configuration(配置)
    properties(属性)		********************常用*************************
    settings(设置)		********************常用*************************	
    typeAliases(类型别名)  ********************常用*************************
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)	********************常用*************************
        environment(环境变量)
            transactionManager(事务管理器)
            dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)		********************常用*************************

常用的必须通读官方文档。

补充:

1.dataSource里面的字面值属性优先级>>>外部的properties文件里面的属性的优先级>>>properties里面的子节点的属性

2.一个配置完整的 settings 元素的示例如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

3.mapper标签:这些配置会告诉了 MyBatis 去哪里找映射文件也就是对应每个class的xml文件(写具体sql语句的文件),剩下的细节就应该是每个 SQL映射文件了,也就是接下来我们要讨论的。

2.xml映射文件

3.动态SQL

4.Java API

5.日志

8.概念定义补充

1.域对象

​ 域对象是所有应用程式的中枢。他们捕捉了数据库的核心数据模型和应用在数据上的业务规则。在典型情况下,应用程式的大多数子系统都依赖这些通用的域对象–这意味着域对象的映射越接近数据库大纲,应用程式研发者理解和使用他们就越容易,因为他们表现了数据库中的现实"实体"和"关系"。

​ 在Java中可以说是可以拥有增删改查功能的一个Javabean类。Java中的三个域对象:servletContext—request—session。在数据库中域对象可以理解为就是一张表里面的一条数据信息。

2.持久化

什么是持久化

​ 持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。

​ 狭义的理解: “持久化”仅仅指把域对象永久保存到数据库中;广义的理解,“持久化”包括和数据库相关的各种操作。
● 保存:把域对象永久保存到数据库。
● 更新:更新数据库中域对象的状态。
● 删除:从数据库中删除一个域对象。
● 加载:根据特定的OID,把一个域对象从数据库加载到内存。
● 查询:根据特定的查询条件,把符合查询条件的一个或多个域对象从数据库加载内在存中。

为什么要持久化?

持久化技术封装了数据访问细节,为大部分业务逻辑提供面向对象的API。
● 通过持久化技术可以减少访问数据库数据次数,增加应用程序执行速度;
● 代码重用性高,能够完成大部分数据库操作;
● 松散耦合,使持久化不依赖于底层数据库和上层业务逻辑实现,更换数据库时只需修改配置文件而不用修改代码。

3.ORM

​ 对象关系映射(英语:(Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换 。从效果上说,它其实是创建了一个可在编程语言里使用的–“虚拟对象数据库”。

​ 面向对象是从软件工程基本原则(如耦合、聚合、封装)的基础上发展起来的,而关系数据库则是从数学理论发展而来的,两套理论存在显著的区别。为了解决这个不匹配的现象,对象关系映射技术应运而生。

​ 简单的说:ORM相当于中继数据。

4.映射

​ 通俗的说,就是同一种蔬菜土豆的不同表现,生土豆到熟土豆。在Java后台和数据库交互之间的映射就是说,将JavaBean类的属性翻译为数据库中的属性。本质上是一样的,只是类型的转变。方便交互统一交流。也可以比喻为英语和汉语,hello和你好,本质上意思一样,但是只会说汉语的可能不明白hello,所以要将hello转为汉语,才能明白。这就是类型的转变。本质没有改变,只是为了更好的沟通。在技术层面上可能涉及到由复杂到简单的表现存储等。

5.依赖

​ 举个例子,我饿了,我要吃食物。那么现在食物就是能够让我生存的依赖。我依赖于食物生存。食物就是依赖。我依赖于食物。对于一个Java项目而言,往往需要很多不同的jar包来实现业务逻辑功能,而对于项目而言,jar包就是这个项目的依赖。添加依赖即为添加jar包。

6.OGNL

​ OGNL(对象图导航语言)是Object-Graph Navigation Language的缩写,它是一种功能强大的表达式语言,通过它简单一致的表达式语法,可以存取对象的任意属性,调用对象的方法,遍历整个对象的结构图,实现字段类型转化等功能。它使用相同的表达式去存取对象的属性。

9.缓存

一级缓存是存在一个sqlsession中的

二级缓存是存在多个sqslsession中的,跨级。范围更广

https://blog.csdn.net/llziseweiqiu/article/details/79413130 参考

Mybatis的一级缓存和二级缓存执行顺序

1、先判断二级缓存是否开启,如果没开启,再判断一级缓存是否开启,如果没开启,直接查数据库

2、如果一级缓存关闭,即使二级缓存开启也没有数据,因为二级缓存的数据从一级缓存获取

3、一般不会关闭一级缓存

4、二级缓存默认不开启

5、如果二级缓存关闭,直接判断一级缓存是否有数据,如果没有就查数据库

6、如果二级缓存开启,先判断二级缓存有没有数据,如果有就直接返回;如果没有,就查询一级缓存,如果有就返回,没有就查询数据库;

https://blog.csdn.net/jinhaijing/article/details/84257981 参考