MyBatis学习使用记录

中文文档

https://mybatis.org/mybatis-3/zh/index.html

1、简介

什么是MyBatis

  • MyBatis 是一个优秀的基于 java 的持久层框架,内部封装了 jdbc,开发者只需要关注 sql 语句 本身,而不需要处理加载驱动、创建连接、创建 statement、关闭连接,资源等繁杂的过程。
  • MyBatis 通过 xml 或注解两种方式将要执行的各种 sql 语句配置起来,并通过 java 对象和 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由mybatis框架执行sql并将结果映射为 java 对象并返回。
  • MyBatis 本是 apache 的一个开源项目 iBatis, 2010 年这个项目由 apache software foundation 迁 移到了 google code,并且改名为 MyBatis 。2013 年 11 月迁移到 GithubiBATIS 一词来源于“internet”和“abatis”的组合,是一个基于 Java 的持久层框架。iBATIS 提供的 持久层框架包括 SQL MapsData Access Objects(DAOs)

MyBatis解决的主要问题

减轻使用 JDBC 的复杂性,不用编写重复的创建 Connetion , Statement ; 不用编写关闭资源代码。 直接使用 java 对象,表示结果数据。让开发者专注 SQL 的处理。 其他分心的工作由 MyBatis 代劳。

  • MyBatis可以完成

    • 注册数据库的驱动,例如 Class.forName(“com.mysql.jdbc.Driver”))
    • 创建 JDBC 中必须使用的 Connection , Statement, ResultSet 对象
    • 从 xml 中获取 sql,并执行 sql 语句,把 ResultSet 结果转换 java 对象
    List<Student> list = new ArrayLsit<>();
    ResultSet rs = state.executeQuery(“select * from student”);
    while(rs.next){
          
          
     Student student = new Student();
     student.setName(rs.getString(“name”));
     student.setAge(rs.getInt(“age”));
     list.add(student);
    }
    
    • 关闭资源
    ResultSet.close() , Statement.close() , Conenection.close()
    

2、入门案例(CRUD)

基本步骤

创建数据库表

在这里插入图片描述

新建测试类和实体类

在这里插入图片描述

package com.phz.entity;

/**
 * @author PengHuAnZhi
 * @createTime 2020/11/10 8:53
 * @projectName MyBatis
 * @className Student.java
 * @description TODO
 */
public class Student {
    
    
    private String id;
    private String name;
    private Integer age;
    private String email;

    public String getId() {
    
    
        return id;
    }

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

    public String getName() {
    
    
        return name;
    }

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

    public Integer getAge() {
    
    
        return age;
    }

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

    public String getEmail() {
    
    
        return email;
    }

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

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

修改pom文件

<?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>org.example</groupId>
    <artifactId>MyBatisDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.49</version>
        </dependency>
    </dependencies>
    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory><!--所在的目录-->
                <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

在这里插入图片描述
特别注意目录名必须对应

编写Dao接口

package com.phz.dao;

import com.phz.entity.Student;

/**
 * @author PengHuAnZhi
 * @createTime 2020/11/10 11:07
 * @projectName MyBatis
 * @className StudentDao.java
 * @description TODO
 */
public interface StudentDao {
    
    
    /*查询所有学生*/
    Student selectStudent(String id);
    int insertStudent(Student student);
    int deleteStudent(int id);
    int updateStudent(String id,String name,Integer age,String email);
}

编写Dao接口Mapper映射文件StudentDao.xml

IDEA是不会编译srcjava目录的xml文件,所以在MyBatis的配置文件中找不到xml文件!(也有可能是Maven构建项目的问题,网上教程很多项目是普通的Java web项目,所以可以放到src下面也能读取到)
在这里插入图片描述

<?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">
<!--
 namespace:必须有值,自定义的唯一字符串
 推荐使用:dao 接口的全限定名称
-->
<mapper namespace="com.phz.dao.StudentDao">
    <!--
 <select>: 查询数据, 标签中必须是 select 语句
 id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称,
 使用名称表示要执行的 sql 语句
 resultType: 查询语句的返回结果数据类型,使用全限定类名
 -->
    <select id="selectStudents" resultType="com.phz.entity.Student">
        <!--要执行的 sql 语句-->
        select id,name,email,age from student where id = #{id}
    </select>
    <insert id="insertStudent">
        insert into student(id,name,email,age)
        values (#{id},#{name},#{email},#{age})
    </insert>
    <delete id="deleteStudent">
        delete from student where id=#{id}
    </delete>
    <update id="updateStudent">
        update student set name = #{name},age = #{age},email = #{email} where id=#{id}
    </update>
</mapper>

创建MyBatis主配置文件

项目 src/main 下创建 resources 目录,设置 resources 目录为 resources root 创建主配置文件:名称为 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>
    <!--settings:控制mybatis全局行为-->
    <settings>
        <!--设置mybatis输出日志-->
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
    <!--配置 mybatis 环境-->
    <environments default="mysql">
        <!--id:数据源的名称-->
        <environment id="mysql">
            <!--配置事务类型:使用 JDBC 事务(使用 Connection 的提交和回滚)-->
            <transactionManager type="JDBC"/>
            <!--数据源 dataSource:创建数据库 Connection 对象
                type: POOLED 使用数据库的连接池,MyBatis会创建一个数据库连接池,连接池的一个连接将会被用作数据库操作。一旦数据库操作完成,MyBatis会将此连接返回给连接池。在开发或测试环境中经常用到此方式
                      UNPOOLED MyBatis会为每一个数据库操作创建一个新的连接,并关闭它。该方式适用于只有小规模数量并发用户的简单应用程序上
                      JNDI MyBatis会从在应用服务器向配置好的JNDI数据源DataSource获取数据库连接。在生产环境中优先考虑这种方式。
            -->
            <dataSource type="POOLED">
                <!--连接数据库的四个要素-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis_test?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--告诉 mybatis 要执行的 sql 语句的位置-->
        <mapper resource="StudentDao.xml"/>
    </mappers>
</configuration>

测试

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;

/**
 * @author PengHuAnZhi
 * @createTime 2020/11/9 11:23
 * @projectName MyBatis
 * @className Test.java
 * @description TODO
 */
public class MyTest {
    
    
    @Test
    public void testSelect() throws IOException {
    
    
        //1、mybatis主配置文件
        String config = "mybatis-config.xml";
        //2、读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(config);
        //3、创建SqlSessionFactory对象,目的是获取SqlSession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //4、读取SqlSession,SqlSession能执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //5、执行SqlSession的selectList()
        Student student = sqlSession.selectOne("com.phz.dao.StudentDao.selectStudents", "2018011875");
        //6、循环输出查询结果
        System.out.println(student.toString());
        //7、关闭SqlSession,释放资源
        sqlSession.close();
    }

    @Test
    public void testInsert() throws IOException {
    
    
        //1、mybatis主配置文件
        String config = "mybatis-config.xml";
        //2、读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(config);
        //3、创建SqlSessionFactory对象,目的是获取SqlSession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //4、读取SqlSession,SqlSession能执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        Student student = new Student();
        student.setId("2018011320");
        student.setName("319宿舍");
        student.setAge(2);
        student.setEmail("[email protected]");
        int result = sqlSession.insert("com.phz.dao.StudentDao.insertStudent", student);
        //mybatis默认不是自动提交事务的, 所以在insert ,update ,delete后要手工提交事务
        sqlSession.commit();
        System.out.println(result);
        sqlSession.close();
    }

    @Test
    public void testDelete() throws IOException {
    
    
        //1、mybatis猪配置文件
        String config = "mybatis-config.xml";
        //2、读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(config);
        //3、创建SqlSessionFactory对象,目的是获取SqlSession
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //4、读取SqlSession,SqlSession能执行sql语句
        SqlSession sqlSession = sqlSessionFactory.openSession();
        //5、执行sql语句
        int result = sqlSession.delete("com.phz.dao.StudentDao.deleteStudent", "2018011320");
        sqlSession.commit();
        System.out.println(result);
        sqlSession.close();
    }

    @Test
    public void testUpdate() throws IOException {
    
    
        String config = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(config);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        String id = "2018011875";
        Student student = sqlSession.selectOne("com.phz.dao.StudentDao.selectStudents", id);
        if (student != null) {
    
    
            student.setAge(30);
            int result = sqlSession.update("com.phz.dao.StudentDao.updateStudent",student);
            System.out.println(result);
        }
        //不写提交默认回滚,起不到更新的目的
        sqlSession.commit();
        sqlSession.close();
    }
}

相关API

Resources

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

SqlSessionFactory

SqlSessionFactory的创建 , 需要使用SqlSessionFactoryBuilder对象的build() 方法 。 由于 SqlSessionFactoryBuilder 对象在创建完工厂对象后,就完成了其历史使命,即刻被销毁。所以,一般会将该 SqlSessionFactoryBuilder 对象创建为一个方法内的局部对象,方法结束,对象销毁。

SqlSessionFactory

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

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

SqlSession

SqlSession 接口对象用于执行持久化操作。一个 SqlSession 对应着一次数据库会话,一次会话以 SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。

SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。 SqlSession 在方法内部创建,使用完毕后关闭。

使用MyBatisUtil对象

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

/**
 * @author PengHuAnZhi
 * @createTime 2020/11/10 21:57
 * @projectName MyBatis
 * @className MybatisUtil.java
 * @description TODO
 */
public class MybatisUtil {
    
    
    private static SqlSessionFactory sqlSessionFactory = null;
    static {
    
    
        try{
    
    
            String config = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(config);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
    
    
        SqlSession sqlSession = null;
        if(sqlSessionFactory != null){
    
    
            //创建一个默认提交事务的SqlSession
            sqlSession = sqlSessionFactory.openSession(true);
        }
        return sqlSession;
    }
}
@Test
public void testSelect() throws IOException {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    //5、执行SqlSession的selectList()
    Student student = sqlSession.selectOne("com.phz.dao.StudentDao.selectStudents", "2018011875");
    //6、循环输出查询结果
    //studentList.forEach(student -> System.out.println(student));
    System.out.println(student.toString());
    //7、关闭SqlSession,释放资源
    sqlSession.close();
}

Dao开发

就是编写一个StudentDao接口的实现类,并实现里面写的数据操作方法,需要的时候直接调用就可以,下面仅实现一个查询方法,其他同理

package com.phz.Service;

import com.phz.dao.StudentDao;
import com.phz.entity.Student;
import com.phz.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

/**
 * @author PengHuAnZhi
 * @createTime 2020/11/10 22:05
 * @projectName MyBatis
 * @className StudentDaoImpl.java
 * @description TODO
 */
public class StudentDaoImpl implements StudentDao {
    
    
    private static StudentDaoImpl studentDao;

    static {
    
    
        if (studentDao == null) {
    
    
            studentDao = new StudentDaoImpl();
        }
    }

    public static StudentDaoImpl getInstance() {
    
    
        return studentDao;
    }

    @Override
    public Student selectStudent(String id) {
    
    
        SqlSession sqlSession = MybatisUtil.getSqlSession();
        Student student = sqlSession.selectOne("com.phz.dao.StudentDao.selectStudents", "2018011875");
        sqlSession.close();
        return student;
    }

    @Override
    public int insertStudent(Student student) {
    
    
        return 0;
    }

    @Override
    public int deleteStudent(int id) {
    
    
        return 0;
    }

    @Override
    public int updateStudent(String id, String name, Integer age, String email) {
    
    
        return 0;
    }
}

测试

@Test
public void testSelect() throws IOException {
    
    
    System.out.println(StudentDaoImpl.getInstance().selectStudent("2018011875").toString());
}

思考:Dao 的实现类其实并没有干什么实质性的工作,它仅仅就是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 idSQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的。

  • 所以,MyBatis 框架就抛开了 Dao 的实现类,直接定位到映射文件 mapper 中的相应 SQL 语句,对 DB 进行操作。这种对 Dao 的实现方式称为 Mapper 的动态代理方式。 Mapper 动态代理方式无需程序员实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代 理实现的。

3、Mybatis框架Dao代理

代码改造

Dao代理实现CRUD,不需要手动编写接口的实现类对象,直接调用SqlSession的getMapper方法,即可获取指定接口的实现类对象。

使用Dao代理修改插入方法:

@Test
public void testInsert() throws IOException {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = new Student();
    student.setId("2018011320");
    student.setName("319宿舍");
    student.setAge(2);
    student.setEmail("[email protected]");
    int result = studentDao.insertStudent(student);
    //mybatis默认不是自动提交事务的, 所以在insert ,update ,delete后要手工提交事务
    sqlSession.commit();
    System.out.println(result);
    sqlSession.close();
}

分析原理:
在这里插入图片描述

查看MapperProxy源码
在这里插入图片描述
在这里插入图片描述

查看execute方法源码:
在这里插入图片描述

深入解析Mapper映射文件

parameterType

<select id="selectStudents" resultType="com.phz.entity.Student" parameterType="String">
    <!--要执行的 sql 语句-->
    select id,name,email,age from student where id = #{
    
    id}
</select>

这个属性是可选的,因为 MyBatis 可以推断出具体传入语句的参数,默认值为未设置(unset)。接口中方法的参数从 java 代码传入到 mapper 文件的 sql 语句。

其中的参数写法不唯一:

  • intjava.lang.Integer
  • hashmapjava.util.HashMap
  • listjava.util.ArrayList
  • studentcom.phz.entity.Student

select,insert,update,delete都可以使用 parameterType 指定类型

传递参数

一个简单参数

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

Student selectStudent(String studentId);//StudentDao中定义的查询方法
<select id="selectStudents" resultType="com.phz.entity.Student" parameterType="String">
    <!--要执行的 sql 语句-->
    select id,name,email,age from student where id = #{id}
</select>

#{id} , id是自定义的变量名称,和方法参数名无关。

多个参数-@Param

如果是多个参数,就必须通过名称使用该参数,在方法形参前面加入@Param("自定义参数名")

Student selectStudentMultiParam(@Param("studentId") String id , @Param("studentName") String name);
<select id="selectStudentMultiParam" resultType="com.phz.entity.Student">
    select * from student where id = #{studentId} and name = #{studentName}
</select>
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = studentDao.selectStudentMultiParam("2018011875","彭焕智");
    System.out.println(student.toString());
}

多个参数-对象

<select id="selectStudentObject" resultType="com.phz.entity.Student">
    select * from student where id = #{queryId} and name = #{queryName}
</select>
<select id="selectStudentObject" resultType="com.phz.entity.Student">
    select * from student where id = #{queryId,javaType=String,jdbcType=VARCHAR} and name = #{queryName,javaType=String,jdbcType=VARCHAR}
</select>
@Test
public void testSelect() {
    
    
    QueryParam queryParam = new QueryParam();
    queryParam.setQueryId("2018011875");
    queryParam.setQueryName("彭焕智");
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = studentDao.selectStudentObject(queryParam);
    System.out.println(student.toString());
}

多个参数-按位置

<select id="selectStudentLocation" resultType="com.phz.entity.Student">
    select * from student where id = #{arg0} and name = #{arg1}
</select>
Student selectStudentLocation(String id,String name);
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = studentDao.selectStudentLocation("2018011875","彭焕智");
    System.out.println(student.toString());
}

多个参数-Map

<select id="selectStudentMap" resultType="com.phz.entity.Student">
    select * from student where id = #{myId} and name = #{myName}
</select>
Student selectStudentMap(Map<String,Object> map);
@Test
public void testSelect() {
    
    
    Map<String ,Object> map = new HashMap<>();
    map.put("myId","2018011875");
    map.put("myName","彭焕智");
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = studentDao.selectStudentMap(map);
    System.out.println(student.toString());
}

#和$

  • #:占位符,告诉 mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{…}代替 sql 语句的“?”。这样做更安全,更迅速,通常也是首选做法。
<select id="selectStudentById" resultType="com.phz.entity.Student">
    select * from student where id = #{studentId}
</select>
<select id="selectStudentByEmail" resultType="com.phz.entity.Student">
    select * from student where email = #{studentEmail}
</select>
Student selectStudentById(String id);
Student selectStudentByEmail(String email);
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    System.out.println(studentDao.selectStudentById("2018011875").toString());
    System.out.println(studentDao.selectStudentByEmail("[email protected]").toString());
}

转换为mybatis执行的就是

String sql=” select id,name,email,age from student where id=?;
PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1,"2018011875");
  • == 字 符 串 替 换 = = , 告 诉 ‘ m y b a t i s ‘ 使 用 ‘ 字符串替换==,告诉 `mybatis` 使用` ==,mybatis使包含的“字符串”替换所在位置。使用Statementsql语句和${}`的 内容连接起来。主要用在替换表名,列名,不同列排序等操作.
//使用不同列作为查询条件
Student selectStudentByDiffField(@Param("col") String colunName,@Param("cval") Object value);
<select id="selectStudentByDiffField" resultType="com.phz.entity.Student">
    select * from student where ${col} = #{cval}
</select>
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
   	//按照email列查询
    System.out.println(studentDao.selectStudentByDiffField("email","[email protected]"));
}

封装Mybatis输出结果

resultType

在这里插入图片描述
在这里插入图片描述

  • 简单类型(int等)
  • 对象类型(Student等)
  • 如果返回的参数是一个集合,那么需要指定的是集合中的类型,而不是集合本身
<select id="selectAll" resultType="com.phz.entity.Student">
    select * from student;
</select>
List<Student> selectAll();
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    //lambda表达式	
    studentDao.selectAll().forEach((e) -> System.out.println(e));
    studentDao.selectAll().forEach(System.out::println);
}
  • mapsql 的查询结果作为 Mapkeyvalue

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

<select id="selectReturnMap" resultType="java.util.Map">
    select * from student where id = #{studentId}
</select>
Map<Object, Object> selectReturnMap(String id);
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    System.out.println(studentDao.selectReturnMap("2018011875"));
}

在这里插入图片描述

这里查询结果前者是键,如id,后者是值,如2018011875

  • resultMap:可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给指定属性。 常用在列名和 java 对象属性名不一样的情况。
<!-- 创建 resultMap
    id:自定义的唯一名称,在<select>使用
    type:期望转为的 java 对象的全限定名称或别名
-->
<resultMap id="studentMap" type="com.phz.entity.Student">
    <!-- 主键字段使用 id -->
    <id column="id" property="id"/>
    <!--非主键字段使用 result-->
    <result column="name" property="name"/>
    <result column="email" property="email"/>
    <result column="age" property="age"/>
</resultMap>
<!--resultMap: resultMap 标签中的 id 属性值-->
<select id="selectUseResultMap" resultMap="studentMap">
    select * from student where id = #{queryId} or name = #{queryName}
</select>
List<Student> selectUseResultMap(QueryParam queryParam);
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    QueryParam queryParam = new QueryParam();
    queryParam.setQueryId("2018011875");
    queryParam.setQueryName("彭焕智");
    studentDao.selectUseResultMap(queryParam).forEach((e) -> System.out.println(e));
}

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

使用列别名和resultType

创建一个新的实体测试类

package com.phz.entity;

/**
 * @author PengHuAnZhi
 * @createTime 2020/11/11 22:31
 * @projectName MyBatis
 * @className PrimaryStudent.java
 * @description TODO
 */
public class PrimaryStudent {
    
    
    private String stuId;
    private String stuName;
    private Integer stuAge;

    public String getStuId() {
    
    
        return stuId;
    }

    public void setStuId(String stuId) {
    
    
        this.stuId = stuId;
    }

    public String getStuName() {
    
    
        return stuName;
    }

    public void setStuName(String stuName) {
    
    
        this.stuName = stuName;
    }

    public Integer getStuAge() {
    
    
        return stuAge;
    }

    public void setStuAge(Integer
                                  stuAge) {
    
    
        this.stuAge = stuAge;
    }

    @Override
    public String toString() {
    
    
        return "PrimaryStudent{" +
                "stuId='" + stuId + '\'' +
                ", stuName='" + stuName + '\'' +
                ", stuAge='" + stuAge + '\'' +
                '}';
    }
}
List<PrimaryStudent> selectUseFieldAlias(QueryParam queryParam);
<select id="selectUseFieldAlias" resultType="com.phz.entity.PrimaryStudent">
    select id as stuId,name as stuName,age as stuAge from student where id=#{queryId} and name=#{queryName}
</select>
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    QueryParam queryParam = new QueryParam();
    queryParam.setQueryId("2018011875");
    queryParam.setQueryName("彭焕智");
    studentDao.selectUseFieldAlias(queryParam).forEach(System.out::println);
}

使用resultMap

<!-- 创建 resultMap
    id:自定义的唯一名称,在<select>使用
    type:期望转为的 java 对象的全限定名称或别名
-->
<resultMap id="primaryStudentMap"
           type="com.phz.entity.PrimaryStudent">
    <!-- 主键字段使用 id -->
    <id column="id" property="stuId"/>
    <!--非主键字段使用 result-->
    <result column="name" property="stuName"/>
    <result column="age" property="stuAge"/>
</resultMap>
<!--resultMap: resultMap 标签中的 id 属性值-->
<select id="selectUseDiffResultMap" resultMap="primaryStudentMap">
    select * from student where id=#{queryId} or name=#{queryName}
</select>
List<PrimaryStudent> selectUseDiffResultMap(QueryParam param);
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    QueryParam param = new QueryParam();
    param.setQueryId("2018011875");
    param.setQueryName("彭焕智");
    List<PrimaryStudent> stuList;
    stuList = studentDao.selectUseDiffResultMap(param);
    stuList.forEach( stu -> System.out.println(stu));
}

模糊like

  • java 代码中提供要查询的 “%2018011875%
<select id="selectLikeFirst" resultType="com.phz.entity.Student">
    select * from student where id like #{studentId}
</select>
List<Student> selectLikeFirst(String name);
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<Student> studentList = studentDao.selectLikeFirst("%2018011875%");
    studentList.forEach(System.out::println);
}
  • mapper 文件中使用 like name "%" #{xxx} "%"
List<Student> selectLikeSecond(String id);
<select id="selectLikeSecond" resultType="com.phz.entity.Student">
    select * from student where id like "%" #{studentId} "%"
</select>
@Test
public void testSelect() {
    
    
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<Student> studentList = studentDao.selectLikeSecond("2018011875");
    studentList.forEach(System.out::println);
}

MyBatis框架动态SQL

特别注意,在xml中出现大于小于等符号,可能会导致解析出现问题,必须使用对应的实体符号代替!

数学符号 语义 实体符号
< 小于 &it;
> 大于 &gt;
>= 大于等于 &gt;=
<= 小于等于 &it;=

<if>

<select id="selectStudentIf" resultType="com.phz.entity.Student">
    <!--要执行的 sql 语句-->
    select * from student where 1=1
    <if test="name != null and name != ''">
        <!--第一个if中的and可以不写,写上也不会错,系统会将多余的一个and删除,但是剩下的都必须写上-->
        and name = #{name}
    </if>
    <if test="age > 0">
        and age &gt; #{age}
    </if>
</select>
Student selectStudentIf(Student student);
@Test
public void testIf(){
    
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = new Student();
    student.setName("彭焕智");
    student.setAge(20);
    System.out.println(studentDao.selectStudentIf(student).toString());
}

标签的中存在一个比较麻烦的地方:需要在 where 后手工添加 1=1 的子句,不然如果出现后面的if语句全部为false的时候,sql语句就会出错,但是当数据量很大的时候,这样的效率就会很低

<where>

<select id="selectStudentWhere" resultType="com.phz.entity.Student">
    select * from student
    <where>
        <if test="name != null and name != ''">
            and name = #{name}
        </if>
        <if test="age > 0">
            and age &gt; #{age}
        </if>
    </where>
</select>
Student selectStudentWhere(Student student);
@Test
public void testWhere(){
    
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    Student student = new Student();
    student.setName("彭焕智");
    student.setAge(20);
    System.out.println(studentDao.selectStudentWhere(student));
}

<foreach>

List<简单类型>

collection 表示要遍历的集合类型, listarray 等。

opencloseseparator 为对遍历内容的 SQL 拼接。

<select id="selectStudentForList" resultType="com.phz.entity.Student">
    select * from student
    <if test="list != null and list.size > 0">
        where id in
        <foreach collection="list" open="(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </if>
</select>
List<Student> selectStudentForList(List<String> list);
@Test
public void testList(){
    
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<String> list = new ArrayList<>();
    list.add("2018011875");
    list.add("2018011874");
    list.add("2018011876");
    List<Student> studentList = studentDao.selectStudentForList(list);
    studentList.forEach(System.out::println);
}

List<对象>

<select id="selectStudentForObjectList" resultType="com.phz.entity.Student">
    select * from student
    <if test="list != null and list.size >0">
        where id in
        <foreach collection="list" open="(" close=")" separator="," item="student">
            #{student.id}
        </foreach>
    </if>
</select>
List<Student> selectStudentForObjectList(List<Student> studentList);
@Test
public void testObjectList() {
    
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<Student> studentList = new ArrayList<>();
    Student student = new Student();
    student.setId("2018011875");
    studentList.add(student);
    student = new Student();
    student.setId("2018011874");
    studentList.add(student);
    student = new Student();
    student.setId("2018011876");
    studentList.add(student);

    studentDao.selectStudentForObjectList(studentList).forEach(System.out::println);
}

代码片段

标签用于定义 SQL 片断,以便其它 SQL 标签复用。而其它标签使用该 SQL 片断,需要使用 子标签。该标签可以定义 SQL 语句中的任何部分,所以子标签可以放在动态 SQL 的任何位置。

<sql id="studentSql">
    select *
    from student
</sql>
<select id="selectStudentForSqlFragment" resultType="com.phz.entity.Student">
    <include refid="studentSql"/>
    <if test="list != null and list.size >0">
        where id in
        <foreach collection="list" open="(" close=")" separator="," item="student">
            #{student.id}
        </foreach>
    </if>
</select>
List<Student> selectStudentForSqlFragment(List<Student> studentList);
@Test
public void testSqlfragment() {
    
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    List<Student> studentList = new ArrayList<>();
    Student student = new Student();
    student.setId("2018011875");
    studentList.add(student);
    student = new Student();
    student.setId("2018011874");
    studentList.add(student);
    student = new Student();
    student.setId("2018011876");
    studentList.add(student);

    studentDao.selectStudentForSqlFragment(studentList).forEach(System.out::println);
}

MyBatis配置文件

一般情况,数据库的连接四要素都是存放在一个专门的配置文件中,然后MyBatis主配置文件再去读取他。
在这里插入图片描述
在这里插入图片描述

MyBatis支持别名

需要在主配置文件中声明

<typeAliases>
    <!--
        定义单个类型的别名
        tyoe:类型的全限定名称
        alias:自定义别名
    -->
    <typeAlias type="com.phz.entity.Student" alias="student"/>
    <!--
        批量定义别名,扫描整个包下的类,别名为类名(首字母大小写都可以)
        name:包名
    -->
    <package name="com.phz.entity"/>
</typeAliases>

在这里插入图片描述
在这里插入图片描述

小扩展

  • 基于PageHelper的分页

第一步:引入maven坐标

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>

第二步:在主配置文件中的environments节点前加入plugin配置

<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
        <property name="helperDialect" value="mysql"/>
    </plugin>
</plugins>

第三步:使用

@Test
public void testPageHelper(){
    
    
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
    PageHelper.startPage(1,3);
    List<Student> studentList = new ArrayList<>();
    Student student = new Student();
    student.setId("2018011875");
    studentList.add(student);
    student = new Student();
    student.setId("2018011874");
    studentList.add(student);
    student = new Student();
    student.setId("2018011876");
    studentList.add(student);
    studentDao.selectStudentForSqlFragment(studentList).forEach(System.out::println);
}

在这里插入图片描述
将PageHelper.startPage(1,3);改为PageHelper.startPage(1,1);
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_43509535/article/details/112323092