Mybatis

一、什么是MyBatis?

github官网解释:

The MyBatis SQL mapper framework makes it easier to use a relational database with object-oriented applications. MyBatis couples objects with stored procedures or SQL statements using an XML descriptor or annotations. Simplicity is the biggest advantage of the MyBatis data mapper over object relational mapping tools.

中文官网的解释:

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

自己总结:

是一个基于ORM思想,支持自定义SQL,存储过程,基本映射以及高级映射的持久层(数据访问层)框架

框架:对通用代码的封装,提前写好了一堆接口和类,基于这些现有的接口和类进行开发,可以大大提高开发效率

二、为什么要用Mybatis?

2.1、JDBC的不足:

  • sql语句写死在java程序中,不灵活。改SQL的话就要改Java代码,违背开闭原则OCP
  • 代码比较繁琐:例如利用占位符(?)进行复制
  • 封装成Java对象是比较繁琐的

2.2、理解ORM思想:

ORM: 对象 关系 映射

O(Object): JVM中的对象

R(Relational):关系型数据库

M(Mapping):映射 :将java 对象转为数据库表中的一条数据,或者将数据库表中的一条记录转为java对象就称为 映射

简单来说映射就是: 对象和数据库表之间互换转换过程

所以ORM就是:其实就是数据库表中的记录和对象之间的转换,转换的过程就是映射

Mybatis框架就是一个ORM框架,但是它是半自动的,因为程序员需要自己写SQL语句

2.3、为什么要用呢?

首先MyBatis框架是一个半自动化的ORM,程序员可以自己写SQL语句,可以将SQL语句单独写在配置文件中,并且它能够将数据库表中的记录转换为对象,弥补了JDBC封装对象和JDBC中SQL语句不灵活的短板。

三、关于Mybatis的事务管理机制(深度剖析)

* 在mybatis-config.xml文件中,可以通过以下的配置进行mybatis的事务管理

* type属性的值包括两个:

JDBC(jdbc)

MANAGED(managed)

type后面的值,只有以上两个值可选,不区分大小写。

* 在mybatis中提供了两种事务管理机制:

第一种:JDBC事务管理器

第二种:MANAGED事务管理器

* JDBC事务管理器:

mybatis框架自己管理事务,自己采用原生的JDBC代码去管理事务:

conn.setAutoCommit(false); 开启事务。

....业务处理...

conn.commit(); 手动提交事务

使用JDBC事务管理器的话,底层创建的事务管理器对象:JdbcTransaction对象。

如果你编写的代码是下面的代码:

SqlSession sqlSession = sqlSessionFactory.openSession(true);

表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false);

在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true。

如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次。

* MANAGED事务管理器:

mybatis不再负责事务的管理了。事务管理交给其它容器来负责。例如:spring。

我不管事务了,你来负责吧。

对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED

那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。

没有人管理事务就是没有事务。

JDBC中的事务:

如果你没有在JDBC代码中执行:conn.setAutoCommit(false);的话,默认的autoCommit是true。

* 重点:

以后注意了,只要你的autoCommit是true,就表示没有开启事务。

只有你的autoCommit是false的时候,就表示开启了事务。

四、MyBatis第⼀个⽐较完整的代码写法

SqlSession sqlSession = null;
try {
    //获取SqlSessionFactoryBuilder对象
    SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    //获取SqlSessionFactory对象
    /* InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
    sessionFactory = sqlSessionFactoryBuilder.build(is);*/
    SqlSessionFactory sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsReader("mybatis-config.xml"));

    //获取SqlSession对象
    //openSession()方法执行后如果不加参数,则默认开启事务,若有参数true则不开启事务
    sqlSession = sessionFactory.openSession();

    //执行sql语句
    /*List list = sqlSession.selectList("test.selectAll");
    System.out.println(list);*/
    int i = sqlSession.insert("insertOne");
    System.out.println("执行成功影响" + i + "行");
    //提交事务
    sqlSession.commit();


} catch (IOException e) {
    if (sqlSession != null) {
        sqlSession.rollback();
    }
    throw new RuntimeException(e);
}finally {
    // 6.关闭
    if (sqlSession != null) {
        sqlSession.close();
    }
}

 

五、关于MyBatis集成日志组件

5.1、介绍MyBatis中集成的日志组件

目的:让我们调试更加方便

mybatis常见的集成的日志组件有哪些?

SLF4J(沙拉风)

LOG4J

LOG4J2

STDOUT_LOGGING

注意:log4j log4j2 logback都是同一个作者开发的。

* 其中STDOUT_LOGGING是标准日志,mybatis已经实现了这种标准日志。mybatis框架本身已经实现了这种标准。

只要开启即可。怎么开启呢?在mybatis-config.xml文件中使用settings标签进行配置开启。

这个标签在编写的时候要注意,它应该出现在environments标签之前。注意顺序。当然,不需要记忆这个顺序。

因为有dtd文件进行约束呢。我们只要参考dtd约束即可。

这种实现也是可以的,可以看到一些信息,比如:连接对象什么时候创建,什么时候关闭,sql语句是怎样的。

但是没有详细的日期,线程名字,等。如果你想使用更加丰富的配置,可以集成第三方的log组件。

5.2、 引⼊⽇志框架logback

引⼊⽇志框架的⽬的是为了看清楚mybatis执⾏的具体sql。 启⽤标准⽇志组件,只需要在mybatis-config.xml⽂件中添加以下配置:【可参考mybatis⼿册】 标准⽇志也可以⽤,但是配置不够灵活,可以集成其他的⽇志组件,例如:log4j,logback等。

logback日志框架实现了slf4j标准。(沙拉风:日志门面。日志标准。)

第一步:引入logback的依赖。

ch.qos.logback

logback-classic

1.2.11

第二步:引入logback所必须的xml配置文件。

这个配置文件的名字必须叫做:logback.xml或者logback-test.xml,不能是其它的名字。

这个配置文件必须放到类的根路径下。不能是其他位置。

主要配置日志输出相关的级别以及日志具体的格式。

六、 使⽤MyBatis完成CRUD

6.1、以下方式不使用代理类mapper

先将获取SqlSession步骤封装成一个工具类

我们获取SqlSession的步骤:

  • 通过SqlSessionFactoryBuilder 的build(Resources.getResourceAsReader("mybatis-config.xml"))方法获取SqlSessionFactory
  • 再通过SqlSessionFactory 的openSession()方法获取 SqlSession
public class SqlSessionUtil {
    private SqlSessionUtil(){}
    public static SqlSessionFactory sessionFactory ;

    static {
        try {
            sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static SqlSession getSqlSession(){
        return sessionFactory.openSession();
    }
}

定义了一个User类

public class User {
    private Integer id ;
    private String username ;

    private String password ;

    private String gender;

    public User() {
    }

    public User(Integer id, String username, String password, String gender) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.gender = gender;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
        "id=" + id +
        ", username='" + username + '\'' +
        ", password='" + password + '\'' +
        ", gender='" + gender + '\'' +
        '}';
    }
}

 




  

  
    insert into user(username,password,gender)
    values (#{username},#{password},#{gender})
  

  
    delete from user
    where id = #{id}
  

  
    update user set username = #{username},password = #{password},gender = #{gender}
    where id = #{id}
  
public class MyBatisTest {
    @Test
    public void selectById(){
        var sqlSession = SqlSessionUtil.getSqlSession();
        var user = (User) sqlSession.selectOne("selectById", 1);
        System.out.println(user);
        sqlSession.commit();
        sqlSession.close();
    }
    public void updateById(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        User user = new User(7, "wangwu", "asdfas", "男");
        int updateById = sqlSession.update("updateById", user);
        System.out.println(updateById);
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void insertTest(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        //创建传递的实体类
        User user = new User(null, "lisi", "987654", "女");
        //执行sql
        int insert = sqlSession.insert("insertOne", user);
        System.out.println(insert);
        sqlSession.commit();
        sqlSession.close();
    }
    @Test
    public void deleteById(){
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();

        int deleteById = sqlSession.delete("deleteById", 6);
        System.out.println(deleteById);
        sqlSession.commit();
        sqlSession.close();

    }

}

6.2、使用代理类Mapper进行CRUD

工具类使用6.1中的SqlSessionUtil,这里对它进行一个改进:在使用三层架构进行开发过程中,为了保证service和dao中使⽤的SqlSession对象是同⼀个,可以将SqlSession对象存放到 ThreadLocal当中,这样做的目的是为了方便在service中进行事务管理。修改SqlSessionUtil⼯具类 :

使用这个方式在三层架构中就不用创建DAO层的实现类了


public class SqlSessionUtil {
    private SqlSessionUtil(){}
    private static final SqlSessionFactory sessionFactory ;
    private static final ThreadLocal threadLocal;
    static {
        try {
            sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
            threadLocal = new ThreadLocal<>();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if (sqlSession == null) {
            sqlSession = sessionFactory.openSession();
            threadLocal.set(sqlSession);
        }
        return sqlSession;

    }
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
            //注意移除SqlSession对象和当前线程的绑定关系
            //因为Tomcat服务器支持线程池
            threadLocal.remove();
        }
    }
}




  
  

  
    insert into user(username,password,gender)
    values (#{username},#{password},#{gender})
  

  
    delete from user
    where id = #{id}
  

  
    update user set username = #{username},password = #{password},gender = #{gender}
    where id = #{id}
  

public class User {
    private Integer id ;
    private String username ;

    private String password ;

    private String gender;

    public User() {
    }

    public User(Integer id, String username, String password, String gender) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.gender = gender;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
        "id=" + id +
        ", username='" + username + '\'' +
        ", password='" + password + '\'' +
        ", gender='" + gender + '\'' +
        '}';
    }
}
public interface UserMapper {
    List queryAll();
}

七、MyBatis中的一些小技巧

7.1 #{}和${}

#{}:底层使用的是JDBC中的 PreparedStatement 实现,可以避免SQL注入的风险。

PreparedStatement 的特点:有一个预编译的过程,先进行SQL语句的编译,然后给SQL语句的占位符?传值。

${}: 底层使用的是JDBC中 Statement 实现,具有SQLz注入的风险。

Statement 的特点 :先进行SQL语句的拼接,然后再对SQL语句进行编译。

优先使用#{ }:防止SQL注入的风险

那么什么时候使用:

如果需要SQL语句的关键字放到SQL语句中,只能使用${ },因为#{ }是以值的形式放到SQL语句当中的

你可能感兴趣的:(mybatis)