MyBatis学习笔记

本笔记为本人在B站学习时整理而成,为了更好的学习,将其整理成笔记,以防忘记相关知识点。

框架概述

三层架构

界面层(视图层):和用户打交道的, 接收用户的请求参数, 显示处理结果的。(jsp ,html ,servlet)
业务逻辑层:接收了界面层传递的数据,计算逻辑,调用数据库,获取数据。
数据访问层(持久层层): 就是访问数据库, 执行对数据的查询,修改,删除等等的。

三层对应的包
界面层: controller包 (servlet)
业务逻辑层: service 包(XXXService类)
数据访问层: dao包(XXXDao类)

三层中类的交互
用户使用界面层–> 业务逻辑层–>数据访问层(持久层)–>数据库(mysql)

三层对应的处理框架
界面层—servlet—springmvc(框架)
业务逻辑层—service类—spring(框架)
数据访问层—dao类—mybatis(框架)

为什么要使用三层?

(1)结构清晰、耦合度低, 各层分工明确

(2)可维护性高,可扩展性高

(3)有利于标准化

(4)开发人员可以只关注整个结构中的其中某一层的功能实现

(5)有利于各层逻辑的复用

框架(Framework)

框架好比是一个规定好了某些内容的模板,需要根据一定的格式完成需要的内容。框架又好比是提供了许多基础的功能(灯光、背景、音乐)的舞台,加入自己的内容后才是完整的演出。

框架可以看作是一个半成品的软件,项开发者提供了许多可重复使用基础功能,帮助完成某一领域的开发任务。

框架特点:

(1)框架一般不是全能的, 不能做所有事情。

(2)框架是针对某一个领域有效, 特长在某一个方面,比如mybatis做数据库操作强,但是他不能做其它的。

(3)框架是一个软件。

JDBC编程缺陷

(1)代码较多,开发效率低

(2)需要关注Connection、Statement、ResultSet对象的创建与销毁

(3)对ResultSet对象的查询结果需要进行人工封装

(4)重复代码多

(5)业务逻辑代码与数据库操作代码混合在一起

MyBatis框架概述

MyBatis是Apache的一个开源项目,前身是iBatis,是java语言的持久层框架,2013年代码迁移到GitHub上。

mybatis是 MyBatis SQL Mapper Framework for Java (sql映射框架)

(1)sql mapper :sql映射

​ 可以把数据库表中的一行数据 映射为 一个java对象。

​ 一行数据可以看做是一个java对象。操作这个对象,就相当于操作表中的数据。

(2)Data Access Objects (DAOs) :数据访问 , 对数据库执行CRUD。

回顾JDBC编程

public void findStudent() {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            //注册 mysql 驱动
            Class.forName("com.mysql.jdbc.Driver");
            //连接数据的基本信息 url ,username,password
            String url = "jdbc:mysql://localhost:3306/ssm";
            String username = "root";
            String password = "root";
            //创建连接对象
            conn = DriverManager.getConnection(url, username, password);
            //保存查询结果
            List<Student> stuList = new ArrayList<>();
            //创建 Statement, 用来执行 sql 语句
            stmt = conn.createStatement();
            //执行查询,创建记录集,
            rs = stmt.executeQuery("select * from student");
            while (rs.next()) {
                Student stu = new Student();
                stu.setId(rs.getInt("id"));
                stu.setName(rs.getString("name"));
                stu.setAge(rs.getInt("age"));
                //从数据库取出数据转为 Student 对象,封装到 List 集合
                stuList.add(stu);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {

            //关闭资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }

mybatis提供了以下功能:

(1)提供了创建Connection ,Statement, ResultSet的能力 ,不用开发人员创建这些对象了。

(2)提供了执行sql语句的能力, 不用你执行sql。

(3)提供了循环sql, 把sql的结果转为java对象, List集合的能力。

(4)提供了关闭资源的能力,不用你关闭Connection, Statement, ResultSet。

开发人员做的是: 提供sql语句。

总之,mybatis是一个sql映射框架,提供的数据库的操作能力。增强的JDBC,使用mybatis让开发人员集中精神写sql就可以了,不必关心Connection,Statement,ResultSet的创建,销毁,sql的执行。

MyBatis框架快速入门

Mybatis中文官方文档网址:https://mybatis.org/mybatis-3/zh/index.html

MyBatis入门例子

实现步骤

(1) 打开navicat,新建数据库ssm,新建一张student表

	create table student(
		id int primary key auto_increment,
		name varchar(80),
		email varchar(100),
		age int
	)

(2) 在idea中新建空项目,命名为my_ssm_study

(3) 添加module,新建maven工程,选择maven-archetype-quickstart骨架模板

(4) 填写工程坐标:

	GroupId:com.kwxy
	ArtifactId:ch01-mybatis-first
	Version:1.0-SNAPSHOT

(5) Module name设置为:ch01-mybatis-first,点击finish,等待maven项目的构建,控制台显示BUILD SUCCESS字样,则构建成功

(6) 将src/mainsrc/test下的java目录右键Mark Directory as-->Sources Root

(7) 在src/main下新建resources目录,右键Mark Directory as-->Resources Root

(8) 删除默认创建的APP类文件 ,这些文件分别在main和test目录中

(9) 删除pom.xml文件中无关的配置,将编译和运行jdk版本改为1.8

(10) 在build标签中添加’指定资源位置’的配置,配置大致如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.kwxy</groupId>
    <artifactId>ch01-mybatis-first</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!--单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <!--指定资源位置-->
        <resources>
            <resource>
                <directory>src/main/java</directory><!--所在的目录-->
                <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

(11) 加入mybatis依赖和mysql驱动依赖(加完最好右键pom.xml->Maven->Reimport

 	<!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
    </dependency>
    <!--mysql驱动-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.9</version>
    </dependency>

(12) 创建Student类:com.kwxy.domain.Student,生成对应的无参构造,有参构造,set/get,toString方法

package com.kwxy.domain;

public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;

    public Student() {
    }

    public Student(Integer id, String name, String email, Integer age) {
        this.id = id;
        this.name = name;
        this.email = email;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", age=" + age +
                '}';
    }
}

(13) 定义持久层的dao接口,定义操作数据库的方法

//com.kwyx.dao.StudentDao 接口
//查询方法的返回类型一般是带有泛型的List,泛型类型为实体类型。无参数。
//insert方法的返回类型为int,表示修改数据库中记录的行数,参数为实体类对象。
package com.kwxy.dao;

import com.kwxy.domain.Student;

import java.util.List;

public interface StudentDao {
    /**
     * 查询student表的所有的数据
     * @return
     */
    List<Student> selectStudents();
    
    /**
     * 插入方法
     * @param student 表示要插入到数据库的数据
     * @return 表示执行insert操作后的 影响数据库的行数
     */
    int insertStudent(Student student);
}

(14) 创建一个Mybatis使用的xml配置文件,称之为sql映射文件或mapper文件。一般是一张表一个sql映射文件。此文件在接口所在的目录中,文件名与接口名保持一致

<?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.kwxy.dao.StudentDao">
    <!--
       select:表示查询操作。
       id: 你要执行的sql语法的唯一标识, mybatis会使用这个id的值来找到要执行的sql语句
           可以自定义,但是要求你使用接口中的方法名称。

       resultType:表示结果类型的, 是sql语句执行后得到ResultSet,遍历这个ResultSet得到java对象的类型。
                  值写的类型的全限定名称
    -->
    <select id="selectStudents" resultType="com.kwxy.domain.Student" >
        select id,name,email,age from student order by id
    </select>

    <!--插入操作-->
    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>
</mapper>
<!--
  sql映射文件(sql mapper): 写sql语句的, mybatis会执行这些sql
  1.指定约束文件
     <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

    mybatis-3-mapper.dtd是约束文件的名称, 扩展名是dtd的。
  2.约束文件作用:限制,检查在当前文件中出现的标签,属性必须符合mybatis的要求。

  3.mapper 是当前文件的根标签,必须的。
    namespace:叫做命名空间,唯一值的, 可以是自定义的字符串。
               要求你使用dao接口的全限定名称。

  4.在当前文件中,可以使用特定的标签,表示数据库的特定操作。
   <select>:表示查询操作,写的是select语句
   <update>:表示更新操作,写的是update语句
   <insert>:表示插入操作,写的是insert语句
   <delete>:表示删除操作,写的是delete语句
-->

可以看到mapper文件中主要修改三个属性:namespace、id、resultType

(15) 创建mybatis使用的主配置文件。主配置文件提供了连接数据库的信息和SQL映射文件的位置。一般一个项目一个主配置文件
src/main/resources目录下创建mybatis-config.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>

    <!--环境配置: 数据库的连接信息
        default:必须和某个environment的id值一样。
        告诉mybatis使用哪个数据库的连接信息。也就是访问哪个数据库
    -->
    <environments default="mydev">
        <!-- environment : 一个数据库信息的配置, 环境
             id:一个唯一值,自定义,表示环境的名称。
        -->
        <environment id="mydev">
            <!--
               transactionManager :mybatis的事务类型
                   type: JDBC(表示使用jdbc中的Connection对象的commit,rollback做事务处理)
            -->
            <transactionManager type="JDBC"/>
            <!--
               dataSource:表示数据源,连接数据库的
                  type:表示数据源的类型, POOLED表示使用连接池
            -->
            <dataSource type="POOLED">
                <!--
                   driver, user, username, password 是固定的,不能自定义。
                -->
                <!--数据库的驱动类名-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <!--连接数据库的url字符串-->
                <property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
                <!--访问数据库的用户名-->
                <property name="username" value="root"/>
                <!--密码-->
                <property name="password" value="root"/>
            </dataSource>
        </environment>

    </environments>

    <!-- sql mapper(sql映射文件)的位置-->
    <mappers>
        <!--一个mapper标签指定一个文件的位置。
           从类路径开始的路径信息。  target/clasess(类路径)
           前提是要在pom文件中加入指定资源位置的配置,否则找不到文件
           因为不配置的话,src/main/java中的非java文件,不会输出到target/clasess(类路径)下
        -->
        <mapper resource="com/kwxy/dao/StudentDao.xml"/>
    </mappers>
</configuration>
        <!--
           mybatis的主配置文件: 主要定义了数据库的配置信息, sql映射文件的位置

           1. 约束文件
           <!DOCTYPE configuration
                PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                "http://mybatis.org/dtd/mybatis-3-config.dtd">

            mybatis-3-config.dtd:约束文件的名称

          2. configuration 根标签。
        -->

sql映射文件与Dao接口存在对应关系,放在了与Dao接口相同java目录下,因此需要在pom.xml中添加资源插件

 <resources>
    <resource>
      <directory>src/main/java</directory>
      <includes>
        <include>**/*.xml</include>
        <include>**/*.properties</include>
      </includes>
      <filtering>false</filtering>
    </resource>

    <!--原有资源路径-->
  <resource>
    <directory>src/main/resources</directory>
    <includes>
      <include>**/*.xml</include>
      <include>**/*.properties</include>
    </includes>
    <filtering>false</filtering>
  </resource>
</resources>

本来在执行compile命令时,会默认将src/main/resources下的内容复制到target/classes中,但是在学习过程中发现,一旦添加了资源插件,resources目录下的主配置文件mybatis.xml并没有复制到target/classes中。而添加原有资源路径的插件后又成功了。因此建议一旦添加资源插件,必须添加原有资源路径.
这个问题课程中的老师在后面提到了,并给出了一些不同于添加原有资源路径解决的办法:
1)清理后重新编译
2)build---->rebuild
3)file---->invalidate caches重启IDEA
4)直接复制mybatis.xml文件到classes目录。。。。(亏你想得出来)

这些方法2 3 我没尝试过,4我是不屑于尝试的。供诸位参考。

(16) 在src/test/java下的包中创建测试类,创建测试方法通过mybatis向数据库插入一条数据

public class TestMybatis {
    /**
     * 测试方法,测试功能
     */
    @Test
    public void testInsert() throws IOException {
        //访问mybatis读取student数据
        //1.定义mybatis主配置文件的名称, 从类路径的根开始(target/clasess)
        String config="mybatis-config.xml";

        //2.读取这个config表示的文件
        InputStream in = Resources.getResourceAsStream(config);

        //3.创建了SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder builder  = new SqlSessionFactoryBuilder();

        //4.创建SqlSessionFactory对象
        SqlSessionFactory factory = builder.build(in);

        //5.获取SqlSession对象,从SqlSessionFactory中获取SqlSession
        //SqlSession sqlSession = factory.openSession();
        SqlSession sqlSession = factory.openSession(true);//参数为true表示自动提交事务

        //6.【重要】指定要执行的sql语句的标识。  sql映射文件中的namespace + "." + 标签的id值
        String sqlId = "com.kwxy.dao.StudentDao.insertStudent";

        //7. 重要】执行sql语句,通过sqlId找到语句
        Student student  = new Student(1001,"李晶","lijing@163.com",23);
        int nums = sqlSession.insert(sqlId,student);

        //mybatis默认不是自动提交事务的, 所以在insert ,update ,delete后要手工提交事务
        //如果factory.openSession(true),则不需要手动提交
        //sqlSession.commit();

        //8.输出结果
        System.out.println("执行insert的结果="+nums);

        //9.关闭SqlSession对象
        sqlSession.close();
    }
}

(17) 执行测试方法,查看控制台输出信息,并通过navicat查看数据库student表数据信息

#idea控制台输出结果为:  
执行insert的结果=1

#通过navicat查看student表,的确插入了一条数据:
1001	李晶	lijing@163.com	23

配置日志

在上述案例中,如果执行增删改操作,对开发人员来说执行的过程和结果不清晰:不知道执行了哪条sql语句,也不知道数据库发生了什么变化。
因此可以考虑配置日志信息。
在MyBatis的主配置文件mybatis.xml中,<configuration>标签下加入如下配置:

<!--settings:控制mybatis全局行为-->
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>

这样下次对数据库操作就会有具体操作信息输出在控制台。

MyBatis工具类的创建与使用

在之前提供的案例最终访问数据库的过程中,如果写不同方法访问数据库会有大量的代码冗余,主要是从第一步到第五步创建SqlSession对象为止,均是可重复的部分,因此可以创建一个工具类。
在创建工具类时注意重量级对象SqlSessionFactory只创建一次,所以放到静态代码块中。

public class MyBatisUtils {
    private static SqlSessionFactory factory = null;

    static {
        String config = "mybatis-config.xml"; // 需要和你的项目中的文件名一样
        try {
            InputStream in = Resources.getResourceAsStream(config);
            //创建SqlSessionFactory对象,使用SqlSessionFactoryBuild
            factory = new SqlSessionFactoryBuilder().build(in);

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


    //获取SqlSession的方法
    public static SqlSession getSqlSession() {
        SqlSession sqlSession = null;
        if (factory != null) {
            sqlSession = factory.openSession();// 非自动提交事务
        }
        return sqlSession;
    }
}

现在创建测试方法使用工具类通过mybatis查询数据库中学生数据。

@Test
public void testSelect(){
    //获取SqlSession对象,从SqlSessionFactory中获取SqlSession
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    //【重要】指定要执行的sql语句的标识。  sql映射文件中的namespace + "." + 标签的id值
    String sqlId = "com.kwxy.dao.StudentDao.selectStudents";
    //【重要】执行sql语句,通过sqlId找到语句
    List<Student> studentList = sqlSession.selectList(sqlId);
    //输出结果
    studentList.forEach( stu -> System.out.println(stu));//lambda表达式
    //关闭SqlSession对象
    sqlSession.close();
}

执行测试方法,查看控制台结果

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl'adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 1123629720.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@42f93a98]
==>  Preparing: select id,name,email,age from student order by id 
==> Parameters: 
<==    Columns: id, name, email, age
<==        Row: 1001, 李晶, lijing@163.com, 23
<==      Total: 1
Student{id=1001, name='李晶', email='lijing@163.com', age=23}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@42f93a98]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@42f93a98]
Returned connection 1123629720 to pool.

Process finished with exit code 0

MyBatis主要对象介绍

Resources类

Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。

InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

SqlSessionFactoryBuilder

SqlSessionFactory 的 创 建 , 需 要 使 用 SqlSessionFactoryBuilder 对 象 的 build() 方 法 。

由于 SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即可被销毁。

所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。

SqlSessionFactoryBuilder builder  = new SqlSessionFactoryBuilder();
//创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(in);

SqlSessionFactory 接口

SqlSessionFactory 接口对象是一个重量级对象(系统开销大的对象),是线程安全的,所以一个应用 只需要一个该对象即可。

接口实现类: DefaultSqlSessionFactory

SqlSessionFactory作用: 获取SqlSession对象。

SqlSession sqlSession = factory.openSession();

创建 SqlSession 需要使用 SqlSessionFactory 接口的的 openSession()方法。

➢ openSession(true):创建一个有自动提交功能的 SqlSession

➢ openSession(false):创建一个非自动提交功能的 SqlSession,需手动提交

➢ openSession():同 openSession(false)

SqlSession接口

定义了操作数据的方法 例如 selectOne() ,selectList() ,insert(),update(), delete(), commit(), rollback() 。

SqlSession接口的实现类DefaultSqlSession。

使用要求: SqlSession对象不是线程安全的,需要在方法内部使用, 在执行sql语句之前,使用openSession()获取SqlSession对象。

在执行完sql语句后,需要关闭它,执行SqlSession.close(). 这样能保证他的使用是线程安全的。

MyBatis框架Dao代理(重点)

传统dao执行方式

创建DAO接口的实现类,重写其操作数据库的方法:
(1) 根据mybatis的工具类,获得sqlsession。
(2) 指定执行Sql语句的标识。 sql映射文件中的namespace+"."+标签的id值。
(3) 通过sqlId找到sql语句并执行。
(4) 关闭SQL session。
(5) 将结果返回。

以后访问操作数据库时,调用DAO接口的相应方法即可。

public class StudentDaoImpl implements StudentDao {
    @Override
    public List<Student> selectStudents() {
        //获取SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        String sqlId="com.kwxy.dao.StudentDao.selectStudents";
        //执行sql语句, 使用SqlSession类的方法
        List<Student> students  = sqlSession.selectList(sqlId);
        //关闭
        sqlSession.close();
        return students;
    }
    @Override
    public int insertStudent(Student student) {
        //获取SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        String sqlId="com.kwxy.dao.StudentDao.insertStudent";
        //执行sql语句, 使用SqlSession类的方法
        int nums = sqlSession.insert(sqlId,student);
        //提交事务
        sqlSession.commit();
        //关闭
        sqlSession.close();
        return nums;
    }
}
@Test
public void testSelectStudents(){
	StudentDao dao  = new StudentDaoImpl();
    List<Student> studentList  = dao.selectStudents();
    for(Student stu:studentList){
        System.out.println(stu);
    }
}

存在问题:

Dao 的实现类其实并没有干什么实质性的工 作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper

中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。

所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对 DB 进行操作。这种对 Dao 的实现方式称为 Mapper 的动态代理方式。

Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的。

动态代理的应用

动态代理的使用条件分析

当我们创建好DAO接口,并定义好操作数据库的抽象方法时,能否自动创建Dao接口的实现类呢:
1)传统dao执行方式第1步获得sqlsession的步骤是固定的,可以通过工具类获得。
2)可以根据动态代理,获得此接口的全限定名,即sql映射文件namespace的值;也可以获得方法名,即sql映射文件对应语句的id值,也就得到了传统dao执行方式中的第2步的sql标识
3)根据方法的返回类型和sql标识指定的sql标签(select,insert,delete或update)可以确定sqlSession调用的方法(delete?还是selectList?或者是insert?update?)因此传统dao执行方式中的第3步也是可以实现的。
4)传统dao执行方式第4步和第5步是容易实现的。

因此,可以通过动态代理的技术自动创建Dao接口的实现类。

MyBatis的Dao动态代理代码实现

myBatis根据Dao方法的调用,获取执行Sql语句的信息。MyBatis根据Dao接口自动创建实现类,并创建对象。完成sqlSession调用方法,访问数据库。(不需要手动创建Dao接口的实现类)(在Dao接口中尽量不要使用方法重载)

public class TestProxy {
    @Test
    public void testSelectStudents(){
        /**
         * 使用mybatis的动态代理机制, 使用SqlSession.getMapper(dao接口)
         * getMapper能获取dao接口对于的实现类对象。
         */
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
        StudentDao dao  =  sqlSession.getMapper(StudentDao.class);

        //com.sun.proxy.$Proxy2 : jdk的动态代理
        System.out.println("dao="+dao.getClass().getName());
        //调用dao的方法, 执行数据库的操作
        List<Student> students = dao.selectStudents();
        for(Student stu: students){
            System.out.println("学生="+stu);
        }
    }

    @Test
    public void testInsertStudent(){
        SqlSession sqlSession  = MyBatisUtils.getSqlSession();
        StudentDao dao  =  sqlSession.getMapper(StudentDao.class);

        Student student = new Student();
        student.setId(1002);
        student.setName("李慧");
        student.setEmail("lihui@qq.com");
        student.setAge(25);
        int nums = dao.insertStudent(student);
        sqlSession.commit();
        System.out.println("添加对象的数量:"+nums);
    }

}

深入理解参数

参数类型(parameterType)

是mapper文件中sql标签的一个属性,属性值是java数据类型的全限定名或mybatis定义的别名,表示DAO接口中方法的参数类型,例如当我们在Dao接口中定义了根据id查询数据的方法:

Student selectStudentById(int id);

在mapper文件中:

 <select id="selectStudents" parameterType="java.lang.Integer" resultType="com.kwxy.domain.Student">
 	<!--也可以使用parameterType="int"  int是mybatis定义的别名-->
    select id,stdname,stdno,classno from t_student where id =#{id}
 </select>

在实际开发过程中,由于在myBatis中的参数类型可以通过反射机制获得,parameterType属性因此可以不写

一个简单类型参数的传递

简单类型: MyBatis把java的基本数据类型和String都叫简单类型。

在mapper文件获取简单类型的一个参数的值,使用 #{任意字符}

多个参数的传递方式
第一种方式:使用@Param命名参数

当 Dao 接口方法多个参数,需要通过名称使用参数。在方法形参前面加入@Param(“自定义参数名”)。

List<Student> selectStudentsByNameOrAge(@Param("myname") String name, @Param("myage") Integet age);

mapper 文件使用#{自定义参数名}。

<select id="selectStudentsByNameOrAge" resultType="com.kwxy.domain.Student">
	select id, name, age, email from student where name=#{myname} or age=#{myage}
</select>
第二种方式:对象传参

创建一个java对象,这个对象包含多个属性,这样在传参时传入这个Java对象,就将多个属性传入,达到了传递多个参数的目的。注意创建的java对象仍然需要封装。

在获取参数值时,完整的语法格式:#{属性名,javaType="java类型",jdbcType=“jdbc类型”}

例如:#{name,javaType="java.lang.String",jdbcType=“VARCHAR”}

实际上经常使用只写属性名的简化格式#{name},javaType与jdbcType可以通过反射机制获取。

第三种方式按位置传参(了解)

根据抽象方法定义时参数的前后位置(从0开始),在mapper文件中获取参数值,格式:#{arg位置下标}

例如:#{arg0}表示第一个参数的值。

MyBatis3.4及之前的版本没有关键字arg

这种方式不常使用。

第四种方式:使用Map(了解)

创建一个Map<String, Object> params = new HashMap(); 向其存入参数。

例如:params.put("name","zhangsan"); params.put("age",24);

定义抽象方法时将这样一个Map传入。

在Mapper文件中,使用的语法#{Map的key}。例如#{name}得到了**”zhangsan“**这样一个值。

这种方式不常使用。

# 与 $

select id,name, email,age from student where id=#{studentId}

‘#’ 的结果: select id,name, email,age from student where id=?

使用占位符,使用PreparedStatement对象


‘$’ 的结果:select id,name, email,age from student where id=1001

相当于

String sql=“select id,name, email,age from student where id=” + “1001”;

使用的Statement对象执行sql, 效率比PreparedStatement低。

$ 可以替换表名或者列名, 你能确定数据是安全的。可以使用 $

  # 和 $ 区别
  1. #使用 ?在sql语句中做占位的, 使用PreparedStatement执行sql,效率高
  2. #能够避免sql注入,更安全。
  3. $不使用占位符,是字符串连接方式,使用Statement对象执行sql,效率低
  4. $有sql注入的风险,缺乏安全性。
  5. $ 可以替换表名或者列名

封装MyBatis输出结果

resultType

resultType: 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。resultType 和 resultMap,不能同时使用。

处理方式:
1.执行完sql语句,调用resultType的值代表类的无参构造器;
2.将每一条数据的所有字段按字段名调用同名属性的setter,然后放入List。

数据转化成java对象的任意的,不一定是实体类。
1)简单类型
例如分组函数的返回类型为简单类型。
count(*) 的resultType可以是"java.lang.Integer"或者别名"int".

2)对象类型
对于自定义的java对象,用使用其全限定名称作为resultType,也可以自定义别名。

自定义别名
在MyBatis的主配置文件<settings>标签之后添加标签:

<typeAliases>
	<!--每一个typeAlias标签代表一个别名设置-->
	<typeAlias type="原类型的全限定名称" alias="别名"/>

	<!--另外一种别名的设置方法:package标签。
		表示一个包下的类名(不区分大小写)可以作为别名使用。
		package标签也可以使用多次
	-->
	<package name="包名"/>
</typeAliases>

应尽量避免使用别名,而是使用全限定名称,避免别名相同产生不必要的麻烦。

3)Map

sql 的查询结果作为 Map 的 key 和 value。推荐使用 Map<Object,Object>。

注意:Map 作为接口返回值,sql 语句的查询结果最多只能有一条记录。大于一条记录是错误。

resultType = "java.util.HashMap"

resultMap

结果映射。指定列名与java属性的对应关系。
1)自定义列值赋给哪个属性。
2)当属性名与列名不同时,必须通过这种方式指定映射关系。

在mapper文件中的mapper标签下:

<!--首先定义出一个Map映射关系-->
<!--id属性是一个自定义的标识,可以在select标签中引用,
	resultType是要与查询结果的列名建立对应关系的属性所在的实体类的完全限定名
-->
<resultMap id="myStudentMap" type="com.kwxy.domain.MyStudent">
      <!--列名和java属性的关系-->
	  <!--主键列与属性的对应使用id标签-->
      <id column="id" property="stuid" />
      <!--非主键列,使用result-->
      <result column="name" property="stuname" />
      <result column="email" property="stuemail" />
      <result column="age" property="stuage" />
</resultMap>

<select id="selectMyStudent" resultMap="myStudentMap">
	 select id,name, email,age from student
</select>

rusultType与resultMap不能同时存在

当使用多表联查的时候,要使用resultMap

通过 属性成员 将2个类建立起联系

一对一映射使用:association

例如:

<!-- Person类的成员属性是Department类的实例 -->
<!-- 实现外部映射,数据库字段和java属性映射 -->
	<resultMap type="com.neu.entity.Person" id="myMap">
		<!--column是数据库字段名,property是java实体类属性名 -->
		<id column="id" property="id" />
		<result column="name" property="name" />
		<result column="age" property="age" />
		<result column="address" property="address" />
		<result column="time" property="time" />
		<result column="path" property="path" />

		<!-- 外键一对一映射,通过association映射 -->
      
		<!-- property的值是创建的对象属性 javaType指定该属性的类型 -->
		<association property="dept" javaType="com.neu.entity.Department">
			<id column="dept_id" property="dept_id" />
			<result column="dept_name" property="dept_name" />
		</association>

一对多映射使用:collection

实体类属性名与列名不同的处理方式

方法一:如上所述,使用resultMap
方法二:在SQL语句中,使用关键字as使用列别名的方式, resultType的默认原则是同名的列值赋值给同名的属性, 使用列别名(java对象的属性名)

as可以省略,但是不建议,增加可读性

模糊查询

回顾sql中模糊查询:

关键字:like

通配符:(1)% 表示任意多个字符 (2)_ 表示任意一个字符

MyBatis实现模糊查询:

方法1:java代码指定like内容

例如指定查询姓名中带"李"的:

java文件中:

String name = "%李%";
List<Student> students = dao.selectByVagueName(name);

而在mapper文件中通过占位符#{…}获取查询的内容:

<select id="selectByVagueName" resultType="com.kwxy.domain.Student"> 
	select id, name,  age, email from student where name like #{name}
</select>

sql语句预编译成了 like '%李%'

方法2:在mapper文件中拼接like的内容

java文件中:

String name = "李";
List<Student> students = dao.selectByVagueName(name);

而在mapper文件中通过占位符#{…}获取查询的内容:

<select id="selectByVagueName" resultType="com.kwxy.domain.Student"> 
	select id, name,  age, email from student where name like "%"#{name}"%"
</select>

sql语句预编译成了 like "%"'李'"%"

MyBatis框架之动态SQL(重点)

动态sql: sql的内容是变化的,可以根据条件获取到不同的sql语句。主要是where部分发生变化。

动态SQL之if

使用案例:

Dao中定义了一个查询方法,参数是一个实体类型对象:

List<Student> selectIf(Student stu);

现要求根据此对象属性的值的内容不同来修改查询条件:
1)当name属性不为空时,添加查询条件name字段与name属性值相同;
2)当age属性值大于0时,添加查询条件age字段的值大于age属性值。

解决方法,在mapper文件中:

<select id="selectIf" resultType="com.kwxy.domain.Student">
	select id,name,age, email, from student where 1=1

	<!--test属性作为判读那条件,如果为true,则添加此标签下的sql语句到逐语句。否则不添加。-->
	<!--在test属性值中,使用resultType中实体类的属性名作为属性值进行条件判断-->
    <!--无法去掉多余的 and ,or-->
	<if test="name != null and name != '' ">
		and name=#{name}
	</if>
	<if test="age > 0">
		and age > #{age}
	</if>
</select>

where 1=1是为了防止第一个条件不成立,而后面的条件成立造成sql语法错误。因此添加一个永真条件,并在原来的第一个查询(本例中的name=#{name})条件前加and,因为像这种if无法去掉多余的 and ,or的。

动态SQL之where

用来代替sql语句中的where关键字,包含if标签。当有一个if标签的test条件成立,就会自动给sql语句中添加where关键字。当第一个if条件判断不成立时,会自动去掉第一个成立的if标签sql片段的连接词(and、or)。

例如之前案例中的select标签可以写成:

<select id="selectWhere" resultType="com.kwxy.domain.Student">
	select id,name,age, email, from student
	<where>
		<if test="name != null and name != '' ">
			name = #{name}
		</if>
		<if test="age > 0">
			and age > #{age}
		</if>
	</where>
</select>

即使第一个if不成立,第二个if成立,MyBatis也会自动生成语法正确的sql语句并执行。就不需要再写一个类似 ”1=1“ 永真的条件加在sql主句中了。

动态SQL之foreach

foreach主要是为了完成sql语句中in关键字包含的数目不定的字段的值。

例如查询id是1001、1002、1003的学生信息,使用SQL语句:select * from student where id in (1001, 1002, 1003);

类似这样的字段值,我们一般保存在List或数组中,然后将List或数组作为Dao接口中抽象方法的参数。

foreach标签是帮助MyBatis遍历这个List或数组,并将其中的元素按sql的语法格式组合在sql语句中。
例如上述例子,我们在mapper文件中:

<select id="selectForeach" resultType="com.kwxy.domain.Student">
     select * from student where id in
     <foreach collection="list" item="myid" open="(" close=")" separator=",">
         #{myid}
     </foreach>
</select>

collection属性的值只能是listarray

当集和中保存到是实体对象时,通过#{item.属性名}的方式获得其值。

其”.属性名“等同于调用此属性的getter方法。

其中:

#collection: 表示接口中的方法参数的类型, 如果是数组使用array , 如果是list集合使用list
#item: 自定义的,表示数组和集合成员的变量
#open: 循环开始时的字符
#close: 循环结束时的字符
#separator: 集合成员之间的分隔符

动态SQL之代码片段

这是sql语句复用的一种方式。

(1) 在mapper标签下:

<sql id="studentSql">select * from student</sql> <!--id是自定义的标识。-->

(2) 在要使用sql标签中包含的sql片段的地方使用如下代码:

<include refid="studentSql"/> <!--其中refid是要使用的sql标签的id属性值。-->

MyBatis框架配置文件

主配置文件

settings:配置myBatis全局行为。参加官方参考文档3.1.2(了解即可,大部分设置使用默认值)

dataSource标签

见MyBatis入门例子中的第 (15) 步MyBatis主配置文件。

事务

见MyBatis入门例子中的第 (15) 步MyBatis主配置文件。

使用数据库属性配置文件

将数据库的连接信息放在一个单独的文件中进行管理。和myBatis主配置文件分开,便于修改、保存,或者处理多个数据库信息。
配置文件的文件名:XXX.properties
配置文件的文件内容:等同于JDBC中提到的配置文件的内容。采用key=value的形式,key使用"."进行多级分隔。例如,在resources目录创建jdbc.properties文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.user=root
jdbc.password=root     

在主配置文件中,首先在configuration标签下指定jdbc.properties文件的位置信息:

<properties resource="配置文件从类路径(target/classes)开始的相对目录路径" />

然后在property标签中使用到value的位置用${key}来代替:

<!--数据库的驱动类名-->
<property name="driver" value="${jdbc.driver}"/>
<!--连接数据库的url字符串-->
<property name="url" value="${jdbc.url}"/>
<!--访问数据库的用户名-->
<property name="username" value="${jdbc.user}"/>
<!--密码-->
<property name="password" value="${jdbc.password}"/>

typeAliases(类型别名)

见resultType处的定义

mappers(映射器)

当有多个mapper文件时:
方法一:写多个mapper标签,如下:

<mapper resource="com/kwxy/dao/XXXDao.xml"/>
<mapper resource="com/kwxy/dao/YYYDao.xml"/>
<mapper resource="com/kwxy/dao/ZZZDao.xml"/>

方法二:使用包名。

<package name="com.kwxy.dao" /> 
<!--可以将com.kwxy.dao这个包下的所有.xml文件一次性加载给MyBatis-->

使用package的要求:

(1) mapper文件名称需要和接口名称一样, 区分大小写的一样

(2) mapper文件和dao接口需要在同一目录

MyBatis中mapper配置文件名称和Dao接口名称是否要一致及原因

(1) 当核心配置文件mapper标签下以resource形式指向依赖配置文件是,不需要一致,

这样就可以加载到其相应的依赖配置文件通过namespace找到其相应的办法。

(2) 如果mapper标签下以package包扫描形式时,需要。

原因如下:

(1) 包扫描形式时,实体类+Mapper接口通过动态代理调用方法

(2) 调用方法时会找其相应的映射配置文件

(3) 当多个mapper接口和mapper.xml同时存在,如果没有相同的名称,则动态代理就不能通过

其一一对应的依赖配置文件创建其相应的实现方法

总结:MyBatis中mapper配置文件名称和Dao接口名称最好一致。

扩展

PageHelper

完成数据分页功能。

(1) 在Maven中添加PageHelper依赖

<!-- mybatis分页插件依赖 -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>4.1.4</version>
    </dependency>
<!-- mybatis分页插件依赖 -->

(2) 在MyBatis主配置文件中加入plugin插件配置

<!--在<environments>之前加入-->
<!--注意这里要写成PageHelper,因为我用的4.x版本 -->
<!--5.0之前的版本都是写PageHelper, 5.0之后要换成PageInterceptor-->
<plugins>
 <plugin interceptor="com.github.pagehelper.PageHelper" />
</plugins>

(3) 使用

查询语句之前调用 PageHelper.startPage 静态方法。

除了 PageHelper.startPage 方法外,还提供了类似用法的 PageHelper.offsetPage 方法。

在你需要进行分页的 MyBatis 查询方法前调用 PageHelper.startPage 静态方法即可

紧跟在这个方法后的第一个 MyBatis 查询方法会被进行分页

在获取到dao接口对象之后,执行抽象方法之前,调用静态方法PageHelper.StartPage,调用抽象方法之后就自动实现了分页查询。

PageHelper.StartPage(int pageNum, int pageSize);//pageNum表示页数,从1开始,pageSize表示每页行数。
@Test
public void testSelect() throws IOException {
    //获取第 1 页,3 条内容
    PageHelper.startPage(1, 3);
    List<Student> studentList = studentDao.selectStudents();
    studentList.forEach(stu -> System.out.println(stu));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

筱晶哥哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值