mybatis学习笔记

MyBatis


文章目录


请点击这里 获取本文代码

第一章 框架概述

1.三层架构

MVC基本概念

​ web开发中,使用mvc架构模式. Model:数据, View:视图, Controller:控制器.

  • Controller控制器:: 接收请求,调用service对象, 显示请求的处理结果.当前使用servlet作为控制器
  • View视图: 现在使用jsp,html,css,js.显示请求的处理结果,把m中的数据显示出来.
  • Model数据:来自mysql,来自文件,来自网络.
MVC作用
  1. 实现解耦合.
  2. 让mvc各司其职.每个模块只负责各自的功能
  3. 使得系统的扩展性更好,更容易维护
三层架构
  1. 界面层(视图层):接受用户的请求,调用service,显示请求的处理结果的.包含jsp,html,servlet等对象. 对应的包controller.
  2. 业务逻辑层: 处理业务逻辑,使用算法处理数据的.把数据返回给界面层.对应的是service包,包中很多的XXXService类.例如: StudentService, OrderService,ShopService
  3. 持久层(数据库访问层): 访问数据库, 或者读取文件, 访问网络,获取数据.对应的包是dao. 例如:StudentDao, OrderDao, ShopDao等等.

2.三层架构请求的处理流程

用户发起请求
视图层
业务逻辑层
持久层
数据库:mysql

3.为什么要使用三层?

  1. 结构清晰,耦合度低,各层分工明确
  2. 可维护性高,可扩展性高
  3. 有利于标准化
  4. 开发人员可以值关注整体结构中的其中一层的功能实现
  5. 有利于于各层逻辑的复用

4.三层架构模式和框架

每一层对应着一个框架
  • 界面层—SpringMVC框架
    • SpringMVC数据SpringFrameWork 3.0版本加入的一个模板,为Spring框架提供了构建web应用程序的能力.
  • 业务层—Spring框架
    • Spring框架是为了解决软件开发的复杂性而创建的.Spring使用的是基本的JavaBean来完成以前非常复杂的企业级开发.
  • 持久层—MyBatis框架
    • mybatis是一个优秀的基于java的持久层框架,内部封装了jdbc,开发者只需要关注sql本身,而不需要处理加载驱动, 创建连接, 创建statement,关闭连接.

5.框架

1.什么是框架(framework)

​ 框架:就是一个软件,完成了部分的功能.软件中的类与类之间的发发调用都已经规定好了.通过这些可以完成某些功能.框架可以看作是模板.

​ 框架是可以升级和改造的.框架是安全的.

​ 框架是对某一方面有用的,不是全能的.需要结合其他的框架一起使用.

6.框架能解决的问题

  1. 框架能实现技术的整合.
  2. 框架能够提高开发的效率,降低难度.

7.jdbc访问数据库的优缺点

优点:
  1. ​ 直观,容易理解
缺点:
  1. 要创建很多对象 Connetcion,Statment,ResultSet
  2. 注册驱动
  3. 执行sql语句
  4. 把ResultSet转为对象,List集合.
  5. 关闭资源
  6. sql语句和逻辑代码混在一起

8.MyBatis框架

1.基本概述

​ mybatis是一个持久层框架,可以操作数据库,操作数据库的增删改查.可以看作是高级的jdcb,解决jdcb的缺点. 它本是Apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了 google code, 并改名为MyBatis. 2013年11月迁移到github.

2.Mybatis解决的主要问题
  1. 注册驱动
  2. 创建jdbc中使用的Connection,Statement,ResultSet对象.
  3. 执行sql语句,得到ResultSet对象
  4. 处理ResultSet, 把记录集中的数据转为java对象, 同时还能把java对象放入到集合.
  5. 关闭资源.
  6. 实现sql语句和java代码解耦合.(mybatis中,将sql语句放到xml文件)
3.Mybatis的官网

​ 官网: https://mybatis.org/mybatis-3/zh/getting-started.html

maven:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

第二章 MyBatis入门

2.1 第一个例子

实现步骤:
  1. 创建student表(id,name,email,age)
  2. 新建maven项目
  3. 修改pom.xml
    1. 加入依赖mybatis依赖, mysql驱动,单元测试junit

      <?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.nguyenxb</groupId>
        <artifactId>ch01-first</artifactId>
        <version>1.0-SNAPSHOT</version>
      
      <!--jdk版本-->
        <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>
      <!--    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>
      
        </dependencies>
      
          <!--  资源插件  在build中配置resources,来防止我们资源导出失败的问题-->
          <build>
              <resources>
                  <resource>
                      <!--  在src/main/resources 目录下可以包含properties、xml文件-->
                      <directory>src/main/java</directory>
                      <includes>
                          <include>**/*.properties</include>
                          <include>**/*.xml</include>
                      </includes>
                      <filtering>false</filtering>
                  </resource>
              </resources>
          </build>
      
      </project>
      
      
    2. <build>中加入资源插件

  4. 创建实体类Student.定义属性,属性名和列名保持一致
  5. 创建Dao接口,定义操作数据库的方法
  6. 创建xml文件(mapper文件),写sql语句.
    1. mybatis框架推荐是把sql语句和java代码分开
    2. mapper文件:定义和dao接口在同一目录,一个表一个mapper文件
  7. 创建mabatis的主配置文件(xml文件):有一个,放在resources目录下.
    1. 定义创建连接示例的数据源(DataSoure)对象
    2. 指定其他mapper文件的位置
  8. 创建测试内容.
    1. 使用main方法,测试mybatis访问数据
    2. 也可以使用junit访问数据库
配置文件详解
文件目录

在这里插入图片描述

mybatis.xml文件

mybatis主配置文件: mybatis.xml, 存放路径:src\main\resources\mybatis.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">
<!-- 1.约束文件: 可以打开看的
    http://mybatis.org/dtd/mybatis-3-config.dtd
    约束文件的作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序-->
<configuration>

    <settings>
        <!--    配置日志 : name 的值是固定的, 
			value 是 STDOUT_LOGGING  : 直接将mybatis操作数据库的日志输出到控制台 -->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
<!--            配置数据源: 创建Connection对象-->
            <dataSource type="POOLED">
<!--                driver:驱动的内容-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
<!--                连接数据库的url: 不能直接使用 & 符号,要将其转换为 html 实体即 &amp; 代表的就是 & 符号 -->
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
<!--                用户名-->
                <property name="username" value="root"/>
<!--                用户密码-->
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
<!--    指定其他mapper文件的位置:
            目的: 使用这里的连接数据库信息来执行其他文件的sql语句
-->
    <mappers>
<!--
    使用mapper的resource属性指定mapper文件的路径.
    这个路径是从target/classes路径开始的
        使用注意:
            resource = "mapper文件的路径,使用 / 分割路径"
            一个mapper resource 指定一个mapper文件
-->
        <mapper resource="com\nguyenxb\dao\StudentDao.xml"/>
    </mappers>
</configuration>
StudentDao.xml

存放操作数据库的增删改查语句

<?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">
<!--
    1.约束文件: 可以打开看的
    http://mybatis.org/dtd/mybatis-3-mapper.dtd
    约束文件的作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序

    2.mapper是根标签
        namespace: 命名空间,必须有值,不能为空.唯一值.
                    推荐使用Dao接口的全限定名称.
        作用: 参与识别sql语句的作用.

    3.在mapper里面可以写<insert>,<update>,<delete>,<select>等标签.
    <insert>里面是 insert语句, 表示执行 insert操作
    <update>里面是 update语句,
    <delete>里面是 delete语句,
    <select>里面是 select语句,
    这些标签对于出现的顺序没有限制,并且可以出现多次
-->
<mapper namespace="com.nguyenxb.dao.StudentDao">
<!--    查询一个学生的Student
    <select> : 表示查询操作,里面是select 语句
    id : 要执行的sql语句的唯一标识,是一个自定义字符串
        推荐使用dao接口中的方法名称
    resultType:告诉mybatis,执行sql语句,把数据赋值给哪个类型的java对象.
        resultType的值现在使用的java对象的全限定名称

    全限定名称: 就是mybatis通过反射机制创建resultType中的对象,并且将语句中的同名字段
    赋值给新创建的对象.然后mybatis再将数据返回
	#{studentId} 就是 占位符 
-->
    <select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
        select id,name,email,age from student where id = #{studentId}
    </select>
</mapper>


Student.java

学生实体类

package com.nguyenxb.entity;

public class Student {
    // 属性和列名一样
    private Integer id;
    private String name;
    private String email;
    private Integer 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 +
                '}';
    }
}
StudentDao.java

学生查询数据库的接口

package com.nguyenxb.dao;

import com.nguyenxb.entity.Student;

public interface StudentDao {
    // 查询一个学生
    Student selectStudentById(Integer id);
}
MyTest.java
package com.nguyenxb;

import com.nguyenxb.entity.Student;
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 org.junit.Test;

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

public class MyTest {
    // 类名不要Test,否则与注解可能会发生冲突
    // 测试mybatis执行sql语句
    @Test
    public void testSelectStudentById() throws IOException {
        // 调用mybatis某个对象的方法,执行mapper文件中的sql语句
        // mybatis 核心类 : SqlSessionFactory

        // 1 定义mybatis主配置文件的位置,从类路径开始的相对路径
        String config = "mybatis.xml";
        // 2 读取主配置文件,使用mybatis框架中的Resources类
        InputStream inputStream = Resources.getResourceAsStream(config);
        // 3 创建SqlSessionFactory对象, 使用SqlSessionFactoryBuilder对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

        // 4 获取 SqlSession对象
        SqlSession sqlSession = factory.openSession();

        // 5 指定要执行的sql语句的 id
        // sql的id = mapper的  namespace+"."+<select>|update|insert|delete标签的id属性
        String sqlId = "com.nguyenxb.dao.StudentDao"+"."+"selectStudentById";

        // 6 通过sqlSession 执行sql语句
        Student student = (Student)sqlSession.selectOne(sqlId,1001);

        System.out.println("使用mybatis查询:"+student.toString());

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

    }
}

运行结果详解:

在这里插入图片描述

2.2 概念

基本概念
  1. 自动提交 : 当你的sql语句执行完毕后, 立即提交事务. 数据库更新操作直接保存到数据库.
  2. 手动 (手工) 提交: 在你需要提交事务的位置,执行方法, 提交事务或者回滚事务.mybatis默认手工提交事务, 在执行 insert, update, delete 语句时需要提交事务
示例:

注 : 如果无法执行代码,请往前查看其他相关的配置

在StudentDao.xml中添加下面的操作数据库的语句以及对应的标签

<mapper namespace="com.nguyenxb.dao.StudentDao">
<!--    添加 insert
     insert into student values(1003,"哈哈","haha@qq.ocm",20)

     如果传入给 mybatis 是一个java对象, 使用#{属性名} 获取次属性的值.
     属性值放到 #{} 占位符的位置, mybatis 执行此属性,对应的getXxx().
     例如 #{id} , 执行 getId();
-->
    <insert id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </insert>
</mapper>

将此段代码放到一个类中,并导入junit的jar包即可运行

 @Test
    public void testInsertStudent() throws IOException {
        // 定义mybatis主配置文件的位置
        String config = "mybatis.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(config);
        // 使用 sqlSessionFactoryBuilder的build方法 创建sqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取 sqlSession 对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        // 创建 sql id ,插入数据
        String sqlId = "com.nguyenxb.dao.StudentDao"+"."+"insertStudent";
        // 创建要插入数据的对象
        Student student = new Student();
        student.setId(1004);
        student.setName("阿瑟东");
        student.setEmail("asd@qq.com");
        student.setAge(23);
        // 执行插入操作
        int rows = sqlSession.insert(sqlId,student);
        // 提交事务
        sqlSession.commit();
        System.out.println("使用mybatis添加一个学生,rows:"+rows);

        sqlSession.close();
    }

执行成功后,数据库将直接插入一个学生对象

解决MyBatis找不到文件问题(idea版本)
  1. 首先检查mybatis.xml中的映射路径,是否正确配置.

  2. 检查XxxDao.xml中的是否加入相关的sql语句映射, 或者 java代码中的namespace与sql标签对应的id是否一致 (注意 : namespace 与 sql标签的id之间 有一个 .连接)

  3. 在pom.xml中加入资源插件

      <!--  资源插件  在build中配置resources,来防止我们资源导出失败的问题-->
        <build>
            <resources>
                <resource>
                    <!--  在src/main/resources 目录下可以包含properties、xml文件-->
                    <directory>src/main/java</directory>
                    <includes>
                        <include>**/*.properties</include>
                        <include>**/*.xml</include>
                    </includes>
                    <filtering>false</filtering>
                </resource>
            </resources>
        </build>
    
  4. 重新build工程在这里插入图片描述

  5. 打开工程目录, 点击pom.xml, 右键 ,点Maven, 点击Reload project

在这里插入图片描述

  1. 重新加载maven
    在这里插入图片描述

  2. 重新启动idea
    在这里插入图片描述

  3. 手动复制文件在这里插入图片描述

  4. 重新创建一个新的项目

2.3 MyBatis对象分析

对象

Resource , SqlSession,SqlSessionFactory等

1 Resource对象

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

​ 定义mybatis主配置文件的位置,从类路径开始的相对路径

String config = "mybatis.xml";

​ 读取主配置文件,使用mybatis框架中的Resources类

InputStream inputStream = Resources.getResourceAsStream(config);
2 SqlSessionFactory对象与SqlSessionFactoryBuilder对象
  • 获取SqlSessionFactory对象,重要对象, 他是一个 重量级对象: 创建此对象需要使用更多的资源和时间.在项目中有一个就可以了.
  • SqlSessionFactory接口: 作用是sqlSession的工厂, 就是创建SqlSession对象.
  • DefaultSqlSessionFactory实现类
public class DefaultSqlSessionFactory implements SqlSessionFactory {}

​ SqlSessionFactory接口中常用的方法:

// 获取一个默认的SqlSession对象,默认是需要手工提交事务的
SqlSession openSession(); 
// boolean 参数表示是否自动提交事务, 
/// true :  创建一个自动提交事务的SqlSession
// false : 等同于没有参数的openSession
SqlSession openSession(boolean var1);

​ 可以使用使用SqlSessionFactoryBuilder对象 来创建使用SqlSessionFactory对象

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
3 SqlSession对象
  • 获取 SqlSession对象, 通过SqlSessionFactory对象的openSession方法
SqlSession sqlSession = factory.openSession();
  • SqlSession对象是通过SqlSessionFactory获取的, SqlSession本身是接口

实现类:DefaultSqlSession, 是线程不安全的

public class DefaultSqlSession implements SqlSession {}

SqlSession对象是提供了大量的执行sql语句的方法:

selectOne: 执行sql语句,最多只能返回1行记录,多于1行就会报错
selectList: 执行sql语句,返回多行数据
selectMap: 执行sql语句,返回一个Map结果
insert:执行insert语句
update: 执行update语句
delete: 执行delete语句
commit: 提交事务
rollback:回滚事务

注意: SqlSession对象是线程不安全的,因此使用步骤如下:

  1. 在方法内部,执行sql语句之前,先获取SqlSession对象
  2. 调用SqlSession的方法,执行sql语句
  3. 关闭SqlSession对象,执行SqlSession.close()
<mapper namespace="com.nguyenxb.dao.StudentDao">
<!--    查询一个学生的Student
    <select> : 表示查询操作,里面是select 语句
    id : 要执行的sql语句的唯一标识,是一个自定义字符串
        推荐使用dao接口中的方法名称
    resultType:告诉mybatis,执行sql语句,把数据赋值给哪个类型的java对象.
        resultType的值现在使用的java对象的全限定名称

    全限定名称: 就是mybatis通过反射机制创建resultType中的对象,并且将语句中的同名字段
    赋值给新创建的对象.然后mybatis再将数据返回
-->
    <select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
        select id,name,email,age from student where id = #{studentId}
    </select>
</mapper>
  • 指定要执行的sql语句的 id , sqlId = mapper中的 namespace+"."+select|update|insert|delete标签的id属性
String sqlId = "com.nguyenxb.dao.StudentDao"+"."+"selectStudentById";
  • 通过sqlSession 执行sql语句
Student student = (Student)sqlSession.selectOne(sqlId,1001);
  • 关闭资源
sqlSession.close();

2.4 mybatis基本用法

mybatis工具类

MyBatisUtil.java

package com.nguyenxb.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;

/**
 * 工具类 : 获取 MyBatis的SqlSession对象
 */
public class MyBatisUtil {

    private static SqlSessionFactory factory = null;
    static {
        String config = "mybatis.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(config);
            factory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * @param autoCommitFlag true:自动提交事务 , false: 手动提交事务
     * @return SqlSession对象
     */
    public static SqlSession getSqlSession(boolean autoCommitFlag){
        SqlSession sqlSession = null;
        if (factory != null){
            if (autoCommitFlag){
                sqlSession = factory.openSession(true);
            }else {
                sqlSession = factory.openSession();
            }
        }
        return sqlSession;
    }

    /**
     * @return 手工提交事务的SqlSession对象
     */
    public static SqlSession getSqlSession(){
        SqlSession sqlSession = null;
        if (factory != null){
            sqlSession = factory.openSession();
        }
        return sqlSession;
    }
}
mapper文件

StudentDao.xml

<?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.nguyenxb.dao.StudentDao">
    <!--Enter your sql statement with (select | insert | update |delete) label-->
    <select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
        select id,name,email,age from student where id = #{studentId}
    </select>
    <select id="selectStudents" resultType="com.nguyenxb.entity.Student">
        select * from student
    </select>
    <select id="insertStudent">
        insert into student values(#{id},#{name},#{email},#{age})
    </select>
</mapper>
XxxDao接口实现类

例: 学生Dao接口实现类 StudentDaoImpl.java

package com.nguyenxb.dao.Impl;

import com.nguyenxb.dao.StudentDao;
import com.nguyenxb.entity.Student;
import com.nguyenxb.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class StudentDaoImpl implements StudentDao {
    @Override
    public Student selectStudentById(Integer id) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        String sqlId = "com.nguyenxb.dao.StudentDao.selectStudentById";
        Student student = sqlSession.selectOne(sqlId, id);
        sqlSession.close();
        return student;
    }

    @Override
    public List<Student> selectStudents() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        String sqlId = "com.nguyenxb.dao.StudentDao.selectStudents";
        List<Student> students = sqlSession.selectList(sqlId);
        sqlSession.close();
        return students;
    }

    @Override
    public int insertStudent(Student student) {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        String sqlId = "com.nguyenxb.dao.StudentDao.insertStudent";
        int rows = sqlSession.insert(sqlId, student);
        sqlSession.commit();
        sqlSession.close();
        return rows;
    }
}

Test类

对 StudentDaoImpl类进行测试

package com.nguyenxb.dao;

import com.nguyenxb.dao.Impl.StudentDaoImpl;
import com.nguyenxb.entity.Student;
import org.junit.Test;

import java.util.List;

public class StudentDaoTest2 {
    @Test
    public void testSelectStudentById(){
        StudentDaoImpl dao = new StudentDaoImpl();
        Student student = dao.selectStudentById(1001);
        System.out.println(student.toString());
    }
    @Test
    public void testSelectStudents(){
        StudentDaoImpl dao = new StudentDaoImpl();
        List<Student> students = dao.selectStudents();
        // lambda 表达式
        students.forEach(student -> System.out.println(student.toString()));
    }
    @Test
    public void testInsertStudent(){
        StudentDaoImpl dao = new StudentDaoImpl();
        Student student = new Student();
        student.setId(1007);
        student.setName("阿萨");
        student.setEmail("as@qq.com");
        student.setAge(30);
        int rows = dao.insertStudent(student);
        System.out.println(rows);
    }
}

第三章 MyBatis的Dao代理

3.1 dao代理

基本概念

​ mybatis提供代理: mybatis创建Dao接口实现类对象,完成对sql语句的执行.

下面通过StudentDaoImpl中的selectStudentById() 方法来了解MyBatis的Dao代理

/*
	String sqlId = "com.nguyenxb.dao.StudentDao.selectStudentById";
Student student = sqlSession.selectOne(sqlId, id);
     测试方法中: 调用dao的方法
    Student student = dao.selectStudentById(1001);
	
mybatis 操作数据库的必要信息: 
    (1) dao: 通过反射可以得到 全限定类型名称 ,dao是StudentDao 类型的, 其全限定名称 com.nguyenxb.dao.StudentDao
    (2) selectStudentById : dao中的方法名称, 方法名称就是 mapper 文件中标签的id, 通过dao.selectStudentById() 能得到 sqlId = "com.nguyenxb.dao.StudentDao.selectStudentById";
    (3) 如何确定调用SqlSession的哪个方法?
        1.根据dao接口的方法返回值,如果返回的是一个对象,例如Student,调用的是SqlSession.selectOne()
        如果dao 接口中的方法返回List,调用SqlSession的selectList();

        2.根据mapper 文件中的标签,如果标签是<insert> , 调用SqlSession.insert()方法

    * */

​ 因此 在mybatis框架中,由于发现使用dao的方法调用就能确定执行sql语句的必要信息,所以mybatis简化了dao对象的实现.
MyBatis的代理技术 (动态代理, dao的动态代理) : 由mybatis 框架在程序执行期间,根据你的Dao接口, 创建一个内存中的接口实现类对象.

举例分析: 由mybatis创建StudentDao接口的实现类 StudentDaoImpl,
使用框架创建的StudentDaoImpl 来代替手工实现的StudentDaoImpl 类的功能,
就不需要开发人员写dao接口的实现类了

实际情况 : mybatis 实际上的创建XxxDao 接口的实现类是 Proxy 类, mybatis在内部通过反射机制实现了 XxxDaoImpl.

使用 MyBatis的 dao 的代理要求:
  1. mapper文件中的namespace : 必须使用dao接口的全限定名称 (namespace+标签id)
  2. mapper文件中标签的id 必须与 dao 接口中的方法名称一样
Mybatis代理的实现方式

使用SqlSession对象的方法 getMapper(XxxDao.class)

​ 例如 : 现在有StudentDao接口, 使用代理的方式: 即不需要人工实现StudentDao接口

// 获取 sqlSession操作数据
SqlSession sqlSession = MyBatisUtil.getSqlSession();
// 使用代理模式
StudentDao studentDao=sqlSession.getMapper(StudentDao.class);
// 查询学生信息
Student student = studentDao.selectStudentById(1001);
System.out.println(student.toString());
sqlSession.close();
// 上面代码中
StudentDao studentDao=sqlSession.getMapper(StudentDao.class);
// 等同于
// 传统方式:即需要实现StudentDao接口, 即需要创建StudentDaoImpl类
 StudentDaoImpl dao = new StudentDaoImpl();

3.2 理解参数

理解参数是: 通过java程序把数据传入到mapper文件中的sql语句. 参数主要是指dao接口的形参.

parameterType

parameterType: 表示参数的类型, 指dao方法的形参数据类型.这个形参的数据类型是给mybatis使用的.mybatis在给sql语句的参数赋值时使用,如 PreparedStatement.setXxx(位置, 值).

dao接口方法传入一个简单参数

​ Dao接口中方法的参数只是一个简单类型(java基本类型和String), 占位符使用 #{任意字符}, 与方法的参数名无关.

例1

dao接口定义

// 根据id 查询一个学生
Student selectStudentById(Integer id);

dao接口使用的是简单类型的参数,mapper文件中要获取这个参数值,使用#{任意字符}

mapper文件

<select id="selectById" parameterType="java.lang.Integer" resultType="com.nguyenxb.entity.Student">
    select id,name,email,age from student where id = #{studentId}
</select>

测试方法

@Test
public void testSelectStudentById(){
    // 获取 sqlSession操作数据
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    // 使用代理模式
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = studentDao.selectStudentById(1001);
    System.out.println(student.toString());
    sqlSession.close();
}
例2

dao接口定义

// 根据email 查询一个学生
Student selectByEmail(String email);

dao接口使用的是简单类型的参数,mapper文件中要获取这个参数值,使用#{任意字符}

mapper文件

<select id="selectByEmail" resultType="com.nguyenxb.entity.Student">
    select id,name,email,age from student where email=#{studentEmail}
</select>

测试方法

  @Test
    public void testOneParameter(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        Student student = dao.selectByEmail("as@qq.com");
        System.out.println(student.toString());
        sqlSession.close();
    }
dao接口方法传入多个简单类型的参数

@Param: 命名参数, 在方法的形参前面使用, 定义参数名.这个名称可以用在mapper文件中.

例1

dao接口定义

/*
* 多个简单类型的参数
* 使用 @Param 命名参数, 注解是mybatis提供的
* 位置: 在形参定义的前面
* 属性:value 自定义的参数名称
* 基本语法格式 :
* @Param(value="myname") 参数
* 当然也可以省略value 即 @Param("myname")
*
* */
List<Student> selectByNameOrAge(@Param(value = "myname") String name,
                                @Param("myage") Integer age);

mapper文件

<!--    多个简单类型的参数
        当使用了@Param 命名后, 例如 @Param(value="myname")
        在maper中, 使用#{命名的参数} 例如#{myname}
-->
    <select id="selectByNameOrAge" resultType="com.nguyenxb.entity.Student">
        select id,name,email,age from student where name=#{myname} or age =#{myage}
    </select>

测试方法

@Test
  public void testSelectByNameOfAge(){
      SqlSession sqlSession = MyBatisUtil.getSqlSession();
      StudentDao dao = sqlSession.getMapper(StudentDao.class);
      List<Student> students = dao.selectByNameOrAge("阿迪",23);
     students.forEach(student -> System.out.println(student.toString()));
      sqlSession.close();
  }
dao接口方法传入一个对象作为参数

方法的形参是一个java对象,这个java对象表示多个参数.使用对象的属性值作为参数使用.

类定义

public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;
    // set | get | toString 方法已省略
}

public class QueryParam {
    private Object p1;
    private Object p2;
     // set | get | toString 方法已省略
}

dao接口定义

/*
* 一个java对象作为参数(对应由属性,每个属性有set,get方法)
* */
List<Student> selectByObject(Student student);

List<Student> selectByObject2(QueryParam param);

mapper文件

<!--    一个java对象作为方法的参数,使用对象的属性作为参数值使用
       简单的语法: #{属性名}, mybatis调用此属性的getXxx方法获取属性值
-->
    <select id="selectByObject" resultType="com.nguyenxb.entity.Student">
        select id,name,email,age from student where name=#{name} or age = #{age}
    </select>
    
    <select id="selectByObject2" resultType="com.nguyenxb.entity.Student">
        select id,name,email,age from student where name =#{p1} or age=#{p2}
    </select>

测试方法

// 例1
@Test
   public void testSelectByObject(){
       SqlSession sqlSession = MyBatisUtil.getSqlSession();
       StudentDao dao = sqlSession.getMapper(StudentDao.class);
       Student student = new Student();
       student.setName("阿迪");
       student.setAge(15);
       List<Student> students = dao.selectByObject(student);
      students.forEach(student1 -> System.out.println(student.toString()));
       sqlSession.close();
   }
// 例2
   @Test
   public void testSelectByObject2(){
       SqlSession sqlSession = MyBatisUtil.getSqlSession();
       StudentDao dao = sqlSession.getMapper(StudentDao.class);
       QueryParam queryParam = new QueryParam();
       queryParam.setP1("阿瑟东");
       queryParam.setP2(15);
       List<Student> students = dao.selectByObject2(queryParam);
      students.forEach(student1 -> System.out.println(student1.toString()));
       sqlSession.close();
   }
此外mybatis的sql语句的参数还支持指定数据类型

例如

#{property,javaType=int,jdbcType=NUMERIC}

<!--
property : 传入的具体值
javaType:java接口的传入值为 int类型,
jdbcType: 数据库的字段类型为 NUMERIC类型
-->

接口定义

List<Student> selectByObject(Student student);

mapper文件

<select id="selectByObject" resultType="com.nguyenxb.entity.Student">
    select id,name,email,age from student where
        name=#{name,javaType=java.lang.String,jdbcType=VARCHAR}
     or
        age = #{age,javaType=java.lang.Integer,jdbcType=INTEGER}
</select>
dao接口中传入多个简单类型的参数,使用位置

参数位置: dao接口中方法的形参列表,从左往右,参数位置是0,1,2….

语法格式: #{arg0} #{arg1}

dao接口定义

List<Student> selectByPosition(String name,Integer age);

mapper文件

<!--    使用位置获取参数值, dao接口方法是多个简单类型的参数
        语法: #{arg0} #{arg1}....
-->
  <select id="selectByPosition" resultType="com.nguyenxb.entity.Student">
         select id,name,email,age from student where name = #{arg0} or age = #{arg1}
    </select>

测试

@Test
public void testSelectByPosition(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    List<Student> students = dao.selectByPosition("阿瑟东",15);
   students.forEach(student1 -> System.out.println(student1.toString()));
    sqlSession.close();
}
dao接口中传入map参数

map作为dao接口的参数,在mapper文件中可以使用key获取参数值

dao接口文件

List<Student> selectStudentByMap(Map<String,Object> map);

mapper文件

<!--    使用map 传递参数
        在 mapper 文件中, 获取 map的值, 是通过key获取的,语法: #{key}
-->
    <select id="selectStudentByMap" resultType="com.nguyenxb.entity.Student">
         select id,name,email,age from student where name = #{myname} or age = #{myage}
    </select>

测试

@Test
    public void testSelectByMap(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        Map<String,Object> map = new HashMap<>();
        map.put("myname","哈哈");
        map.put("myage",23);

        List<Student> students = dao.selectStudentByMap(map);
       students.forEach(student1 -> System.out.println(student1.toString()));
        sqlSession.close();
    }

3.3 #和$的区别

#占位符

语法: #{字符}

mybatis处理#{} 使用jdbc对象是PrepareStatment对象

<select id="selectById" parameterType="java.lang.Integer" resultType="com.nguyenxb.entity.Student">
    select id,name,email,age from student where id = #{studentId}
</select>

mybatis创建出PrepareStatement对象,执行sql语句
String sql = "select id,name,email,age from student where id=?";
PrepareStatement pst = conn.prepareStatement(sql);
pst.setInt(1,1001);// 传递参数
ResultSet rs = pst.executeQuery();// 执行sql语句

#{}的特点:

  • 使用的PrepareStatement对象,执行sql语句,效率高.
  • 使用的PrepareStatement对象,能够避免sql语句注入,sql语句执行更安全.
  • #{} 常常作为列值使用的,位于等号的右侧,#{}位置的值和数据类型有关.
$占位符

语法: ${字符}

mybatis执行${占位符}的sql语句

<select id="selectById" parameterType="java.lang.Integer" resultType="com.nguyenxb.entity.Student">
    select id,name,email,age from student where id = #{studentId}
</select>

${} 表示字符串连接,把sql语句的其他内容和${}内容使用字符串(+) 连接的方式在一起
String sql = "select id,name,email,age from student where id=" + "1001";

mybatis创建Statement对象,执行sql语句
PrepareStatement pst = conn.createStatement(sql);
pst.setInt(1,1001);// 传递参数
ResultSet rs = pst.executeQuery();// 执行sql语句

${}的特点

  • 使用Statement对象,执行sql语句,效率低
  • ${}占位符的值,使用的是字符串连接的方式,有sql注入的风险.有代码安全问题.如传入数据'阿迪' or 1=1
  • ${} 数据是原样使用的,不会区分数据类型.
  • ${} 常用做表名或者列名,在能保证数据安全的情况下使用.

例如:(这是操作数据的例子,很少用)

dao接口

// ${} 占位符的使用
List<Student> queryStudent(@Param("studentName") String name);

mapper文件

<!--    ${} 的使用-->
    <select id="queryStudent" resultType="com.nguyenxb.entity.Student">
        select * from student where name = ${studentName}
    </select>

测试, 传入的是 '阿迪',是有单引号的,并且mybatis不会对其进行任意的数据处理.

@Test
public void testQueryStudent(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    List<Student> students = dao.queryStudent("'阿迪'");

    students.forEach(student -> System.out.println(student.toString()));
    sqlSession.close();

}

举例2 :

假设传入值为 id.

${}一般用途: 用于操作列名,表名, 其对应的sql语句是

select * from student order by id desc,这个操作的才是列

注意: 在使用#{}是没有效果的, 其对应的sql语句是

select * from student order by 'id' desc,这个操作的是字符串

dao接口定义

// ${} 占位符的使用: 操作列名,获取降序排序的学生列表
List<Student> queryStudentByColName(@Param("colName") String name);

mapper

<select id="queryStudentByColName" resultType="com.nguyenxb.entity.Student">
       select * from student order by ${colName} desc
   </select>

测试

    @Test
public void testQueryStudentByColName(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    List<Student> students = dao.queryStudentByColName("id");

    students.forEach(student -> System.out.println(student.toString()));
    sqlSession.close();

}

举例3:

#{}和${}一起使用

dao接口定义

// ${} 占位符的使用: 操作列名
List<Student> queryStudents(
        @Param("tableName")String tableName,
        @Param("name") String name,
        @Param("colName") String colName);

mapper文件

<select id="queryStudents" resultType="com.nguyenxb.entity.Student">
        select * from ${tableName} where name = #{name} order by ${colName} desc
    </select>

测试 : 查询student表中,名字为阿迪,的所有人, 并且降序排序输出, 其sql 语句如下:

select * from student where name = '阿迪' order by id desc

@Test
public void testQueryStudents(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    List<Student> students = dao.queryStudents("student",
            "阿迪","id");

    students.forEach(student -> System.out.println(student.toString()));
    sqlSession.close();

}

3.4 封装MyBatis的输出结果

封装输出结果: MyBatis执行sql语句,得到ResultSet,转为java对象.

resultType
resultType表示自定义对象

查询数据库返回一个对象

resultType属性: 在执行select时使用,作为<select>标签的属性出现.

resultType: 表示结果类型, mysql执行sql语句, 得到java对象的类型. 它的值有两种

(1) java类型的全限定名称, (2)使用别名

 // dao接口中的方法 : 根据id 查询一个学生
    Student selectStudentById(Integer id);

<select id="selectStudentById" resultType="com.nguyenxb.entity.Student">
    select id,name,email,age from student where id = #{studentId}
</select>

resultType: 现在使用java类型的全限定名称. 表示的意思是 mybatis执行sql,把ResultSet中的数据转为Student类型的对象.mybatis会做以下操作:
1.调用com.nguyenxb.entity.Student的无参构造方法,创建对象.
	Student student = new Student(); // 使用反射创建对象
2.同名的列赋值给同名的属性.
	student.setId(rs.getInt("id"));
	student.setName(rs.getString("name"));
3.得到java对象,如果dao接口返回值是List集合,mybatis把student对象放入List集合.

所以执行Student mystudent = dao.selectById(1001);// 得到数据库中 id = 1001 这行数据. 并且将这行数据的列值,赋值给mystudent对象的属性.即我们得到的每一个对象的数据,就是数据库中每一行的值.
resultType返回对象的基本规则

当返回对象时,如果对象的属性名和列名一样,则将列中的数据赋值给对象,如果不一样,则不赋值.

类定义

public class CustomObject {
    private Integer cid;
    private String cname;
    private String email;
    private Integer age;
    // 省略了 set | get |toString 方法
}

dao接口定义

CustomObject selectCustomObjectById(@Param("stuid") Integer id);

mapper文件

<select id="selectCustomObjectById" resultType="com.nguyenxb.pojo.CustomObject">
    select id,name,email,age from student where id = #{stuid}
</select>

测试

 @Test
public void testSelectCustomObjectById(){
    // 获取 sqlSession操作数据
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    // 使用代理模式
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    CustomObject customObject = studentDao.selectCustomObjectById(1001);
    System.out.println(customObject.toString());
    sqlSession.close();
}
//  其结果为 CustomObject{cid=null, cname='null', email='123456@qq.com', age=12}
resultType表示简单类型

查询数据库返回一个值

dao接口定义

// 获取 表中 有行数据
long countStudent();

mapper文件

<!--    执行sql语句,得到一个值,一行一列-->
    <select id="countStudent" resultType="java.lang.Long">
        select count(*) from student
    </select>

测试

@Test
  public void testCountStudent(){
      // 获取 sqlSession操作数据
      SqlSession sqlSession = MyBatisUtil.getSqlSession();
      // 使用代理模式
      StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    long countStudent = studentDao.countStudent();
    System.out.println("行数:"+countStudent);
      sqlSession.close();
  }
// 结果输出 :  行数: 7
resultType表示一个Map结构

例1: 一个map表示一行数据,dao 接口返回是一个map, sql语句最多只能获取一行记录, 多于1行记录会报错

dao接口定义

// 查询结果返回一个Map结构数据
Map<Object,Object> selectMap(@Param("stuid")  Integer id);

mapper文件

<!--
    执行结构得到一个Map结构数据, mybatis执行sql,把ResultSet转为mao
    sql 执行结构是,列名做map的key,列值做value
    sql 执行得到是一行记录,转为map结构是正确的.
-->
    <select id="selectMap" resultType="java.util.Map">
        select id,name,age from student where id = #{stuid}
    </select>

测试

@Test
   public void testSelectMap(){
       // 获取 sqlSession操作数据
       SqlSession sqlSession = MyBatisUtil.getSqlSession();
       // 使用代理模式
       StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
         Map<Object, Object> map = studentDao.selectMap(1005);
        System.out.println("map === "+map);
       sqlSession.close();
   }
// 结果输出 :   map === {name=阿瑟东, id=1005, age=23}
resultMap

resultMap: 结果映射. 自定义列名和java对象属性的对应关系. 常用在列名和属性名不同的情况.

用法:

  1. 先定义resultMap标签,指定列名和属性名称对应关系
  2. 在select标签使用resultMap属性,指定上面定义的resultMap的id值

dao接口定义

CustomObject selectCustomObjectById(@Param("stuid") Integer id);

mapper文件

<!--    使用resultMap定义列和属性的关系-->
<!--    定义resultMap: 一经定义可以反复使用
        id : 给resultMap的映射关系起个名称,唯一值
        type:java类型的全限定名称
-->
    <resultMap id="customMap" type="com.nguyenxb.pojo.CustomObject">
<!--        定义列名和属性名的对应关系-->
<!--        主键类型使用id标签 : 将数据库表的id 赋值给CustomObject类的cid -->
        <id column="id" property="cid"/>
<!--        非主键列使用result标签-->
        <result column="name" property="cname"/>
<!--        列名和属性名相同不用定义,当然定义也没有问题-->
        <result column="email" property="email"/>
        <result column="age" property="age"/>
    </resultMap>
<!--    使用resultMap属性,指定映射关系的id,注 : resultType和 resultMap只能使用一个-->
    <select id="selectCustomObjectById" resultMap="customMap">
        select id,name,email,age from student where id = #{stuid}
    </select>

测试

public void testSelectCustomObjectById(){
    // 获取 sqlSession操作数据
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    // 使用代理模式
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    CustomObject customObject = studentDao.selectCustomObjectById(1001);
    System.out.println(customObject.toString());
    sqlSession.close();
}
//输出:CustomObject{cid=1001, cname='张三', email='123456@qq.com', age=12}

3.5 自定义别名

mybatis提供的对java对象定义简短,好记的名称.

自定义别名步骤:

  1. 在mybatis主配置文件,使用typeAliases标签声明别名
  2. 在mapper文件中,resultType=“别名”

主配置文件:mybatis.xml

<!--别名标签位于<settings>标签之后, 具体顺序可以参照相关的dtd文档-->   
<typeAliases>
<!--        第一种语法格式
            type : java类型的全限定名称 (自定义类型)
            alias:自定义别名

            优点: 别名可以自定义
            缺点:每个类型必须单独定义
-->
<!--        <typeAlias type="com.nguyenxb.entity.Student" alias="stu" />-->
<!--        <typeAlias type="com.nguyenxb.pojo.QueryParam" alias="qp"></typeAlias>-->

<!--        第二种方法
            name : 包名 ,mybatis会把这个包中所有类名作为别名(不用区分大小写)
            优点:使用方便,一次给多个类定义别名
            缺点: 别名不能自定义,必须是类名
-->
        <package name="com.nguyenxb.entity"/>

    </typeAliases>

mapper文件:

<!--在主配置文件配置别名之后,直接使用别名即可-->
<!--第一种方法-->
<select id="selectStudentById" resultType="stu">
    select id,name,email,age from student where id = #{studentId}
</select>

<!--第二种方法-->
<select id="selectStudentById" resultType="student">
    select id,name,email,age from student where id = #{studentId}
</select>

3.6 列名和java对象属性名不一样的解决方式

  1. 使用resultMap:自定义列名和属性名对应关系
  2. 使用resultType: 使用列的别名和java对象的名称一样

dao接口定义

CustomObject selectCustomObjectById2(@Param("stuid") Integer id);

mapper文件

<!--    使用列别名,解决列名和属性名不同的问题-->
 <select id="selectCustomObjectById2" resultType="com.nguyenxb.pojo.CustomObject">
        select id as cid,name cname,email,age from student where id = #{stuid}
    </select>

测试

@Test
public void selectCustomObjectById2(){
    // 获取 sqlSession操作数据
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    // 使用代理模式
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    CustomObject customObject = dao.selectCustomObjectById2(1001);
    System.out.println(customObject);
    sqlSession.close();
}
// 输出:ustomObject{cid=1001, cname='张三', email='123456@qq.com', age=12}

3.7 like 模糊查询

第一种方式

在java程序中, 把like的内容组装好.把这个内容传入到sql语句

dao接口定义

// like第一种方式
List<Student> selectLikeOne(@Param("name") String name);

mapper文件

<select id="selectLikeOne" resultType="com.nguyenxb.entity.Student">
    select * from student where name like #{name}
</select>

测试

@Test
   public void testSelectLikeOne(){
       // 获取 sqlSession操作数据
       SqlSession sqlSession = MyBatisUtil.getSqlSession();
       // 使用代理模式
       StudentDao dao = sqlSession.getMapper(StudentDao.class);
       String name = "%阿%";
       List<Student> students = dao.selectLikeOne(name);
       students.forEach(student -> System.out.println(student));
       sqlSession.close();
   }
/*输出:
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}
Student{id=1008, name='阿迪', email='adi2@qq.com', age=15}
*/
第二种方式

在sql语句种,组织like的内容.

sql语句like的格式: where name like “%” #{name} “%”

注意: “%” #{name} “%” , “%” 与 #{name} 之间存在空格.

dao接口

// like第二种方式
List<Student> selectLikeTwo(@Param("name") String name);

mapper文件

<!--   like 第二种方式-->
<!--对应的sql语句为:select * from student where name like "%" ? "%" -->
<select id="selectLikeTwo" resultType="com.nguyenxb.entity.Student">
        select * from student where name like "%" #{name} "%"
    </select>

测试

@Test
   public void testSelectLikeTwo(){
       // 获取 sqlSession操作数据
       SqlSession sqlSession = MyBatisUtil.getSqlSession();
       // 使用代理模式
       StudentDao dao = sqlSession.getMapper(StudentDao.class);
       String name = "阿";
       List<Student> students = dao.selectLikeTwo(name);
       students.forEach(student -> System.out.println(student));
       sqlSession.close();
   }
/* 输出
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}
Student{id=1008, name='阿迪', email='adi2@qq.com', age=15}
*/

第四章 动态SQL

  • 什么是动态SQL:

动态SQL, 通过mybatis提供的各种标签对条件作出判断以实现动态拼接SQL语句.这里的条件判断表达式为OGNL表达式.常用的动态SQL标签有<if>,<where><choose>,<foreach>等.语法格式与JSTL类似.

  • 说明

同一个dao的方法,根据不同的条件可以表示不同的sql语句,主要是where部分有变化

使用动态sql的时候,dao方法的形参使用java对象.

  • 什么时候使用动态sql?

​ 在多条件查询时使用动态sql

实体符号表

符号说明html实体
<小于&lt;
>大于&gt;
<=小于等于&lt;=
>=大于等于&gt;=

4.1 if标签

语法

注: 只有if,没有else

<if test="boolean判断结果">
    sql 代码
</if>
基本用法

mapper文件中

<select id="selectStudent" resultType="com.nguyenxb.entity.Student">
    select * from student  
    <if test="条件">
        sql语句1
    </if>
     <if test="条件">
        sql语句2
    </if>
</select>

其中 sql语句 : select * from student 称为主sql 语句, if标签中的 sql语句1和sql语句2称为部分sql 语句. if标签可以有多个

<if>标签满足条件时,会将<if> 标签中的部分sql语句拼接至主sql语句的后面

例子

dao接口

// if 使用动态sql 时, 参数使用对象
List<Student> selectIf(Student student);

mapper文件

<!--if
    test: 使用对象的属性值作为条件
-->
    <select id="selectIf" resultType="com.nguyenxb.entity.Student">
        select * from student
        where
        <if test="name != null and name !=''">
            name = #{name}
        </if>
        <if test="age > 0">
            or age &lt; #{age}
        </if>
    </select>

测试

    @Test
    public void selectIf(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        Student student = new Student();
         // 测试1 : 查询成功:查询 名字是阿迪的或者年龄小于20的
        // mybatis生成的sql语句:select * from student where name = ? or age < ?
        student.setName("阿迪");
        student.setAge(20);

        // 测试2 : 查询成功
        // mybatis生成的sql语句:select * from student where name = ?
//        student.setName("阿迪");

        // 测试3 : 查询失败, sql语句语法错误 :
        //mybatis生成的sql语句 : select * from student where or age > 20
//        student.setName(null);
//        student.setAge(20);

        List<Student> students = dao.selectIf(student);
        students.forEach(stu -> System.out.println(stu));
        sqlSession.close();
    }

4.2 where 标签

​ 使用if标签时,容易引起sql语句语法错误. 使用where标签可以解决if产生的语法问题.

​ 使用where标签时,里面是一个或者多个if标签,当有一个if标签 判断条件为true时, where标签会转为 WHERE 关键字 附加到主sql 语句的后面.如果if标签没有一个条件为true, 则忽略添加where和里面的if. where标签会删除 和 它 直接连接的 or | and

语法:
<where>
    <if test="条件1">sql语句1</if>
    <if test="条件2">sql语句2</if>
</where>

dao接口

// where 标签
List<Student> selectWhere(Student student);

mapper文件

<select id="selectWhere" resultType="com.nguyenxb.entity.Student">
 select * from student
     <where>
         <if test="name != null and name !=''">
             or name = #{name}
         </if>
         <if test="age > 0">
             or age &gt; #{age}
         </if>
     </where>
 </select>

测试

      @Test
    public void selectWhere(){
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        StudentDao dao = sqlSession.getMapper(StudentDao.class);
        Student student = new Student();
         // 测试1 : 查询成功, :查询 名字是阿迪的或者年龄大于20的
          // mybatis生成的sql语句 :select * from student WHERE name = ? or age > ?
        student.setName("阿迪");
        student.setAge(20);

          // 测试2: 查询成功
          //mybatis生成的sql语句 : select * from student WHERE name = ?
//          student.setName("阿迪");

        // 测试3 : 查询成功 ,查询年龄大于20的
          // mybatis生成的sql语句 :select * from student WHERE age > ?
        student.setName(null);
        student.setAge(20);

        List<Student> students = dao.selectWhere(student);
        students.forEach(stu -> System.out.println(stu));
        sqlSession.close();
    }

4.3 foreach 循环

java拼接sql语句: 查询 id在idList中的学生信息

@Test
    public void testForEach(){
        List<Integer> idList = new ArrayList<>();
        idList.add(1001);
        idList.add(1002);
        idList.add(1003);

        // 拼接sql语句
        StringBuilder sb = new StringBuilder();
        sb.append("select * from student where id in ");
        sb.append("("); // 循环开始时的字符
        // 追加idList的数据到sql字符串
        for (int i = 0; i < idList.size(); i++) {
            // 获取idList中的成员
            Integer item = idList.get(i);// 集合中的成员
            sb.append(item);
            sb.append(",");//集合成员之间的分隔符
        }
        // 将数据后面的最后一个逗号删除
        sb.deleteCharAt(sb.length()-1);
        sb.append(")");//循环结束时的字符
        System.out.println(sb);
// 输出 : select * from student where id in (1001,1002,1003)
    }
语法格式:
 <foreach collection="集合类型" open="开始的字符" close="结束的字符" item="集合中的成员" separator="集合成员之间的分隔符">
     #{item的值}
                    
</foreach>

标签属性:
collection: 表示 循环的对象是数组还是list集合. 
	如果dao接口方法的形参是 数组, 则 collection="array" .
	如果dao接口方法的形参是 List, 则 collection = "list".
open : 循环开始时的字符. 即 :  sb.append("(");
close: 循环结束时的字符. 即 :  sb.append(")");
item : 集合成员, 自定义的遍历. 即 :  Integer item = idList.get(i);
separator : 集合成员之间的分隔符. 即 :  sb.append(",");
#{item的值}: 获取集合成员的值.

<foreach>标签用于实现对数组与集合的遍历,对其使用,需要注意:

  • collection表示要遍历的集合类型,list, array等.
  • open,close,separator 为对遍历内容的SQL拼接.
  • 一般使用在in语句中.
使用foreach 第一种方式

dao接口定义

List<Student> selectForeach1(List<Integer> idList);

mapper文件

    <select id="selectForeach1" resultType="com.nguyenxb.entity.Student">
        select * from student
        <if test="list != null and list.size > 0">
            where id in
            <foreach collection="list" open="(" close=")" separator="," item="myId">
                #{myId}
            </foreach>
        </if>

    </select>

测试

@Test
public void selectForeach1(){
    List<Integer> idList = new ArrayList<>();
    idList.add(1001);
    idList.add(1002);
    idList.add(1003);

    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    // mybaitis生成的sql语句: select * from student where id in ( ? , ? , ? ) 
    List<Student> students = dao.selectForeach1(idList);
    students.forEach(stu-> System.out.println(stu));
    sqlSession.close();

}
使用foreach 第二种方式

dao接口定义

List<Student> selectForeach2(List<Student> studentList);

mapper文件 , 其中 #{stu.id} , 表示 取出student对象的id属性的值,其他同理.

<select id="selectForeach2" resultType="com.nguyenxb.entity.Student">
       select * from student
       <if test="list != null and list.size > 0">
           where id in
           <foreach collection="list" open="(" close=")" separator="," item="stu">
               #{stu.id}
           </foreach>
       </if>

   </select>

测试

@Test
   public void selectForeach2(){
       List<Student> idList = new ArrayList<>();
    Student student = new Student();
    Student student1 = new Student();
    student.setId(1004);
    student1.setId(1005);
    idList.add(student);
    idList.add(student1);

    SqlSession sqlSession = MyBatisUtil.getSqlSession();
       StudentDao dao = sqlSession.getMapper(StudentDao.class);
       List<Student> students = dao.selectForeach2(idList);
       students.forEach(stu-> System.out.println(stu));
       sqlSession.close();

   }

4.4 代码片段

sql标签标识一段sql代码,可以是表名, 几个字段,where条件都可以. 可以在其他地方复用sql标签的内容.

使用方式:

在mapper文件中定义 sql代码片段: <sql id="唯一字符串">部分sql语句 </sql>

在其他的位置,使用include标签来 引用 某个代码片段

比如, 将使用foreach第一种方式案例的mapper修改如下,也可以完成查询

<!--代码片段定义 --> 
<sql id="SelectStudentField">
        id,name,email,age
    </sql>

    <sql id="selectStudent">
         select <include refid="SelectStudentField"/> from student
    </sql>

<!--使用代码片段-->
<select id="selectForeach1" resultType="com.nguyenxb.entity.Student">
    <include refid="selectStudent"></include>
    <if test="list != null and list.size > 0">
        where id in
        <foreach collection="list" open="(" close=")" separator="," item="myId">
            #{myId}
        </foreach>
    </if>

</select>

第五章 MyBatis配置文件

5.1 配置文件

mybatis配置文件两大类:

  1. mybatis主配置文件

    说明: mybatis主配置文件, 提供mybatis的全局设置.包含的内容,日志,数据源,以及mapper文件的位置

  2. mybatis的mapper文件

    说明: mybatis的mapper文件, 写sql语句的. 一般一个表写一个mapper文件

5.2 setting部分

settings是mybatis的全局设置,影响整个mybatis的运行. 这个设置一般使用默认值就可以了.

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

5.2 typeAliase别名

设置别名

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:

<typeAliases>
  <package name="domain.blog"/>
</typeAliases>

5.3 配置环境

environments: 环境标签, 在它里面可以配置多个environment
	属性: default,必须是某个environment的id属性值.表示mybatis默认连接的数据库.
environment : 表示一个数据库的连接信息.
	属性: id自定义的环境表示.唯一值
transactionManager: 事务管理器
	属性: type 表示事务管理器的类型
	属性值: 
		(1) jdbc: 使用connction对象,由mybatis自己完成事务的处理.
		(2)MANAGED: 管理,表示把数位的处理交给容器实现(由其他软件完成事务的提交,回滚)
dataSource: 数据源, 创建Connection对象,连接数据库.
	属性: type 数据源的类型
	属性值:
		(1) POOLED, mybatis会在内存中创建PooledDataSource类型,管理多个Connection连接对象,使用的是连接池.(常用这个)
		(2) UNPOOLED,不使用连接池,mybatis创建一个UnPooledDataSource这个类,每次执行sql语句下创建Connection对象,在执行sql语句,最后关闭Connection.
		(3) JNDI: java的命名和目录服务


<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC" />
        <!--配置数据源: 创建Connection对象-->
        <dataSource type="POOLED">
            <!--driver:驱动的内容-->
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <!--连接数据库的url: 不能直接使用 & 符号,要将其转换为 html 实体即 &amp; 代表的就是 & 符号 -->
            <property name="url" value="jdbc:mysql://localhost:3306/ssm?
            useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"/>
            <!--用户名-->
            <property name="username" value="root"/>
            <!--用户密码-->
            <property name="password" value="root"/>
        </dataSource>
    </environment>
</environments>

5.4 使用数据库的属性配置文件

需要把数据库的配置信息放到一个单独文件中,独立管理. 这个文件的扩展名是properties.在这个文件中,使用自定义的key=value的格式表示数据

使用步骤:

  1. 在resources目录中,创建 xxx.properties
  2. 在文件中,使用key=value的格式定义数据.
    • 例如: jdbc.url = jdbc:mysql://localhost:3306/springdb
  3. 在mybatis主配置文件,使用properties标签引用外部的属性配置文件
  4. 在使用值的位置,使用${key} 获取key对应的value(即等号右侧的值)

示例:

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?
  useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf-8"
jdbc.username=root
jdbc.password=root

mybatis.xml

在mybatis文件中的configuration的标签里面的第一个标签的位置设置外部配置文件

<!--    使用外部属性配置文件
        resource: 指定类路径下的某个属性的文件
-->
    <properties resource="jdbc.properties"/>

在mybatis.xml中使用外部属性配置文件中的内容,用法如下:

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC"/>
        <!--配置数据源: 创建Connection对象-->
        <dataSource type="POOLED">
            <!--driver:驱动的内容-->
            <property name="driver" value="${jdbc.driver}"/>
            <!--连接数据库的url: 不能直接使用 & 符号,要将其转换为 html 实体即 &amp; 代表的就是 & 符号 -->
            <property name="url" value="${jdbc.url}"/>
            <!--用户名-->
            <property name="username" value="${jdbc.username}"/>
            <!--用户密码-->
            <property name="password" value="${jdbc.password}"/>
        </dataSource>
    </environment>
</environments>

5.5 mapper标签

使用mapper标签指定其他mapper文件的位置

mapper标签使用的格式有两个常用的方式: 注意不要重复加载mapper

<mappers>
<!--        第一种方式, resource="mapper文件的路径
            优点:文件清晰.加载的文件是明确的.文件的位置比较灵活.
            缺点:文件笔记多,代码量会比较大,管理难度大.
"-->
        <mapper resource="com\nguyenxb\dao\StudentDao.xml"/>
<!--        第二种方式, 使用<package>
                name : 包名, mapper文件所在的包名.
                特点:把这个包中的所有mapper文件,一次加载.

                使用要求:
                1.mapper文件和dao接口在同一个目录
                2.mapper文件和dao接口名称完全一样
-->
        <package name="com.nguyenxb.dao"/>
    </mappers>

第六章 PageHelper

​ PageHelper做数据分页.在你的select语句后面加入分页的sql内容,如果你使用的mysql数据库,他就是在select * from student 加入 limit语句.

​ 使用步骤:

  1. 加入pagehelper依赖

    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.1.10</version>
    </dependency>
    
  2. 在mybatis的主配置文件,加入plugins声明 , 注意 : 在environments标签之前加入

    <!--    声明插件-->
        <plugins>
            <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
        </plugins>
    

例子:

dao接口定义

// pageHelper 用法
List<Student> selectAllStudents();

mapper文件

<select id="selectAllStudents" resultType="com.nguyenxb.entity.Student">
    select * from student order by id
</select>

测试

@Test
public void testSelectAllStudents(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao = sqlSession.getMapper(StudentDao.class);
    //调用PageHelper的方法
    PageHelper.startPage(1,3);
    List<Student> students = dao.selectAllStudents();
    students.forEach(stu-> System.out.println(stu));
    sqlSession.close();
}

测试输出:

未调用PageHelper的方法,输出:

mybatis生成的sql语句 : select * from student order by id

/*
Student{id=1001, name='张三', email='123456@qq.com', age=12}
Student{id=1003, name='哈哈', email='haha@qq.ocm', age=20}
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}
Student{id=1008, name='阿迪', email='adi2@qq.com', age=15}
*/

调用PageHelper的方法,输出:

mybatis生成的sql语句 :

​ 当页数不是第一页时, 即如测试2,mybatis生成的sql语句:

​ select * from student order by id LIMIT ?, ?

​ 当页数是第一页时, 即 如 测试1和 3, mybatis生成的sql语句:

​ select * from student order by id LIMIT ?

/*
测试1 : 
调用PageHelper的方法,即PageHelper.startPage(1,3)输出:

Student{id=1001, name='张三', email='123456@qq.com', age=12}
Student{id=1003, name='哈哈', email='haha@qq.ocm', age=20}
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}

测试2 : 
调用PageHelper的方法,即PageHelper.startPage(2,3)输出:

Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
Student{id=1006, name='阿迪', email='adi1@qq.com', age=15}
Student{id=1007, name='阿萨', email='as@qq.com', age=30}	

测试3:
调用PageHelper的方法,即PageHelper.startPage(1,4)输出:

Student{id=1001, name='张三', email='123456@qq.com', age=12}
Student{id=1003, name='哈哈', email='haha@qq.ocm', age=20}
Student{id=1004, name='阿瑟东', email='asd1@qq.com', age=23}
Student{id=1005, name='阿瑟东', email='asd2@qq.com', age=23}
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值