1. 多个参数  81

1.1  需求:通过name和sex查询  81

test中的com.powernode.mybatis.test

StudentMapperTest类中的testSelectByNameAndSex
 //多个参数  81
    @Test
    public void testSelectByNameAndSex(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectByNameAndSex("张三", '男');
        students.forEach(student -> System.out.println(student));
        sqlSession.close();
    }
StudentMapper.xml
<select id="selectByNameAndSex" resultType="Student">
        <!--select * from t_student where name = #{name} and sex = #{age}-->
        <!--select * from t_student where name = #{arg0} and sex = #{arg1}-->
        <!--select * from t_student where name = #{param1} and sex = #{param2}-->
        select * from t_student where name = #{arg0} and sex = #{param2}
    </select>

1.2 通过测试可以看到:81

<select id="selectByNameAndSex" resultType="Student">
        select * from t_student where name = #{name} and sex = #{age}
    </select>

mybatis参数处理2_sql

异常信息描述了:name参数找不到,可⽤的参数包括[arg1, arg0, param1, param2] 

修改StudentMapper.xml配置⽂件:尝试使⽤[arg1, arg0, param1, param2]去参数 

<select id="selectByNameAndSex" resultType="Student">
      select * from t_student where name = #{arg0} and sex = #{arg1}
</select>

mybatis参数处理2_List_02

1.3 根据name和sex查询Student信息得到结论  81

     * 如果是多个参数的话,mybatis框架底层是怎么做的呢?

     *      mybatis框架会自动创建一个Map集合。并且Map集合是以这种方式存储参数的:

     *          map.put("arg0", name);

     *          map.put("arg1", sex);

  或者

     *          map.put("param1", name);

     *          map.put("param2", sex);

arg0 是第⼀个参数

param1是第⼀个参数

arg1 是第⼆个参数

param2是第⼆个参数

1.3.1 注意  81

低版本的mybatis中,使用的是:#{0}和#{1},以及#{2}...

高版本的mybatis中,使用的是:

            #{arg0}

            #{arg1}

            #{arg2}

            #{arg3}

            #{arg4}

            #{param1}

            #{param2}

            #{param3}

            #{param4}

2. 参数之Param注解  82

可以不⽤arg0 arg1 param1 param2吗?这个map集合的key我们⾃定义可以吗?当然可以。使⽤@Param注解即可。这样可以增强可读性。

/**
     * Param注解。   82
     *
     * mybatis框架底层的实现原理:
     *  map.put("name", name);
     *  map.put("sex", sex);
     *
     * @param name
     * @param sex
     * @return
     */
    List<Student> selectByNameAndSex2(@Param("name") String name, @Param("sex") Character sex);

2.1 需求:根据name和age查询  82

test中的com.powernode.mybatis.test

StudentMapperTest类中的testSelectByNameAndSex2

//参数注解之Param  82
    @Test
    public void testSelectByNameAndSex2(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        // mapper实际上指向了代理对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        // mapper是代理对象
        // selectByNameAndSex2是代理方法
        List<Student> students = mapper.selectByNameAndSex2("张三", '男');
        students.forEach(student -> System.out.println(student));
        sqlSession.close();
    }

StudentMapper.xml

<select id="selectByNameAndSex2" resultType="Student">
        <!--使用了@Param注解之后,arg0和arg1失效了-->
        <!--select * from t_student where name = #{arg0} and sex = #{arg1}-->

        <!--使用了@Param注解之后,param1和param2还可以用-->
        <!--select * from t_student where name = #{param1} and sex = #{param2}-->

        select * from t_student where name = #{name} and sex = #{sex}

    </select>

2.2 注意  82

使用了@Param注解之后,arg0和arg1失效了

select * from t_student where name = #{arg0} and sex = #{arg1}

使用了@Param注解之后,param1和param2还可以用

select * from t_student where name = #{param1} and sex = #{param2}

3. Param源码分析  83

mybatis参数处理2_mybatis参数_03

mybatis参数处理2_sql_04

4. 总代码

main中com.powernode.mybatis.mapper

StudentMapper

package com.powernode.mybatis.mapper;

import com.powernode.mybatis.pojo.Student;
import org.apache.ibatis.annotations.Param;

import java.util.Date;
import java.util.List;
import java.util.Map;

//mybatis参数处理  76
public interface StudentMapper {

    /**
     * Param注解。   82
     *
     * mybatis框架底层的实现原理:
     *  map.put("name", name);
     *  map.put("sex", sex);
     *
     * @param name
     * @param sex
     * @return
     */
    List<Student> selectByNameAndSex2(@Param("name") String name, @Param("sex") Character sex);

    /**
     * 这是多参数。   81
     * 根据name和sex查询Student信息。
     * 如果是多个参数的话,mybatis框架底层是怎么做的呢?
     *      mybatis框架会自动创建一个Map集合。并且Map集合是以这种方式存储参数的:
     *          map.put("arg0", name);
     *          map.put("arg1", sex);
     *          map.put("param1", name);
     *          map.put("param2", sex);
     *
     * @param name
     * @param sex
     * @return
     */
    List<Student> selectByNameAndSex(String name, Character sex);

    /**
     * 保存学生信息,通过POJO参数。Student是单个参数。但是不是简单类型。  80
     * @param student
     * @return
     */
    int insertStudentByPOJO(Student student);


    /**
     * 保存学生信息,通过Map参数。以下是单个参数。但是参数的类型不是简单类型。是Map集合。79
     * @param map
     * @return
     */
    int insertStudentByMap(Map<String, Object> map);

    /**
     * 当接口中的方法的参数只有一个(单个参数),并且参数的数据类型都是简单类型。  77-78
     * 根据id查询、name查询、birth查询、sex查询
     */
    List<Student> selectById(Long id);
    List<Student> selectByName(String name);
    List<Student> selectByBirth(Date birth);
    List<Student> selectBySex(Character sex);


}

test中的com.powernode.mybatis.test

StudentMapperTest类

package com.powernode.mybatis.test;

//mybatis参数处理  76

import com.powernode.mybatis.mapper.StudentMapper;
import com.powernode.mybatis.pojo.Student;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 代理模式:
 *      代理对象 链家
 *      代理方法 找房子
 *      目标对象 我
 *      目标方法 找房子
 */
public class StudentMapperTest {

    //参数注解之Param  82
    @Test
    public void testSelectByNameAndSex2(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        // mapper实际上指向了代理对象
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        // mapper是代理对象
        // selectByNameAndSex2是代理方法
        List<Student> students = mapper.selectByNameAndSex2("张三", '男');
        students.forEach(student -> System.out.println(student));
        sqlSession.close();
    }

    //多个参数  81
    @Test
    public void testSelectByNameAndSex(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectByNameAndSex("张三", '男');
        students.forEach(student -> System.out.println(student));
        sqlSession.close();
    }

    // POJO对象参数  80
    @Test
    public void testInsertStudentByPOJO(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        // POJO对象
        Student student = new Student();
        student.setName("张飞");
        student.setAge(50);
        student.setSex('女');
        student.setBirth(new Date());
        student.setHeight(10.0);

        mapper.insertStudentByPOJO(student);
        sqlSession.commit();
        sqlSession.close();
    }

    //参数值Map集合  79
    @Test
    public void testInsertStudentByMap(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        Map<String,Object> map = new HashMap<>();
        map.put("姓名", "赵六");
        map.put("年龄", 20);
        map.put("身高", 1.81);
        map.put("性别", '男');
        map.put("生日", new Date());

        mapper.insertStudentByMap(map);
        sqlSession.commit();
        sqlSession.close();
    }

    //当接口中的方法的参数只有一个(单个参数),并且参数的数据类型都是简单类型。  78
    //根据sex查询
    @Test
    public void testSelectBySex(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        // char --> Character
        Character sex = Character.valueOf('男');
        List<Student> students = mapper.selectBySex(sex);

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

    //当接口中的方法的参数只有一个(单个参数),并且参数的数据类型都是简单类型。  78
    //根据birth查询
    // java.util.Date java.sql.Date,他们都是简单类型。
    @Test
    public void testSelectByBirth() throws Exception{
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);

        //定义日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        //根据日期格式写出日期(将"1980-10-11"字符串转成日期类型)
        Date birth = sdf.parse("1980-10-11");

        List<Student> students = mapper.selectByBirth(birth);

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

    //当接口中的方法的参数只有一个(单个参数),并且参数的数据类型都是简单类型。  78
    //根据name查询
    @Test
    public void testSelectByName(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectByName("李四");
        students.forEach(student -> System.out.println(student));
        sqlSession.close();
    }


    //当接口中的方法的参数只有一个(单个参数),并且参数的数据类型都是简单类型。  77
    //根据id查询
    @Test
    public void testSelectById(){
        SqlSession sqlSession = SqlSessionUtil.openSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> students = mapper.selectById(1L);
        students.forEach(student -> System.out.println(student));
        sqlSession.close();
    }
}

main中com.powernode.mybatis.utils

工具类Utils

package com.powernode.mybatis.utils;

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;

/**
 * MyBatis工具类
 * @author 动力节点
 * @version 1.0
 * @since 1.0
 */
public class SqlSessionUtil {

    private SqlSessionUtil(){}

    private static SqlSessionFactory sqlSessionFactory;

    static {
        try {
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    // 全局的,服务器级别的,一个服务器当中定义一个即可。
    // 为什么把SqlSession对象放到ThreadLocal当中呢?为了保证一个线程对应一个SqlSession。
    private static ThreadLocal<SqlSession> local = new ThreadLocal<>();

    /**
     * 获取会话对象。
     * @return 会话对象
     */
    public static SqlSession openSession(){
        SqlSession sqlSession = local.get();
        if (sqlSession == null) {
            sqlSession = sqlSessionFactory.openSession();
            // 将sqlSession对象绑定到当前线程上。
            local.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 关闭SqlSession对象(从当前线程中移除SqlSession对象。)
     * @param sqlSession
     */
    public static void close(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.close();
            // 注意移除SqlSession对象和当前线程的绑定关系。
            // 因为Tomcat服务器支持线程池。也就是说:用过的线程对象t1,可能下一次还会使用这个t1线程。
            local.remove();
        }
    }

}

main中com.powernode.mybatis.pojo

Student

package com.powernode.mybatis.pojo;

import java.util.Date;

//一个pojo  76
public class Student {
    private Long id;
    private String name;
    private Integer age;
    private Double height;
    private Date birth;
    private Character sex;

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

    public Long getId() {
        return id;
    }

    public void setId(Long 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 Double getHeight() {
        return height;
    }

    public void setHeight(Double height) {
        this.height = height;
    }

    public Date getBirth() {
        return birth;
    }

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Character getSex() {
        return sex;
    }

    public void setSex(Character sex) {
        this.sex = sex;
    }

    public Student(Long id, String name, Integer age, Double height, Date birth, Character sex) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.height = height;
        this.birth = birth;
        this.sex = sex;
    }

    public Student() {
    }
}

StudentMapper.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.powernode.mybatis.mapper.StudentMapper">

    <select id="selectByNameAndSex2" resultType="Student">
        <!--使用了@Param注解之后,arg0和arg1失效了-->
        <!--select * from t_student where name = #{arg0} and sex = #{arg1}-->

        <!--使用了@Param注解之后,param1和param2还可以用-->
        <!--select * from t_student where name = #{param1} and sex = #{param2}-->

        select * from t_student where name = #{name} and sex = #{sex}

    </select>

    <!--
        注意:低版本的mybatis中,使用的是:#{0}和#{1},以及#{2}...
        高版本的mybatis中,使用的是:
            #{arg0}
            #{arg1}
            #{arg2}
            #{arg3}
            #{arg4}

            #{param1}
            #{param2}
            #{param3}
            #{param4}
    -->
    <select id="selectByNameAndSex" resultType="Student">
        <!--select * from t_student where name = #{name} and sex = #{age}-->
        <!--select * from t_student where name = #{arg0} and sex = #{arg1}-->
        <!--select * from t_student where name = #{param1} and sex = #{param2}-->
        select * from t_student where name = #{arg0} and sex = #{param2}
    </select>


    <!--<insert id="insertStudentByPOJO" parameterType="student">-->
    <insert id="insertStudentByPOJO">
        insert into t_student(id,name,age,sex,birth,height) values(null,#{name},#{age},#{sex},#{birth},#{height})
    </insert>

    <!--<insert id="insertStudentByMap" parameterType="map">-->
    <insert id="insertStudentByMap">
        insert into t_student(id,name,age,sex,birth,height) values(null,#{姓名},#{年龄},#{性别},#{生日},#{身高})
    </insert>


    <!--
    List<Student> selectById(Long id);
    List<Student> selectByName(String name);
    List<Student> selectByBirth(Date birth);
    List<Student> selectBySex(Character sex);

    parameterType属性的作用:
        告诉mybatis框架,我这个方法的参数类型是什么类型。
        mybatis框架自身带有类型自动推断机制,所以大部分情况下parameterType属性都是可以省略不写的。

        SQL语句最终是这样的:
            select * from t_student where id = ?
        JDBC代码是一定要给?传值的。
        怎么传值?ps.setXxx(第几个问号, 传什么值);
            ps.setLong(1, 1L);
            ps.setString(1, "zhangsan");
            ps.setDate(1, new Date());
            ps.setInt(1, 100);
            ...
        mybatis底层到底调用setXxx的哪个方法,取决于parameterType属性的值。

    注意:mybatis框架实际上内置了很多别名。可以参考开发手册。
    -->
    <select id="selectById" resultType="Student" parameterType="long">
        select * from t_student where id = #{id}
    </select>

    <select id="selectByName" resultType="student">
        select * from t_student where name = #{name, javaType=String, jdbcType=VARCHAR}
    </select>

    <select id="selectByBirth" resultType="student">
        select * from t_student where birth = #{birth}
    </select>

    <select id="selectBySex" resultType="student">
        select * from t_student where sex = #{sex}
    </select>

</mapper>

pom.xml

<?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.powernode</groupId>
    <artifactId>course16</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.11</version>
        </dependency>
    </dependencies>

    <properties>
        <!--    编译代码使用的jdk版本-->
        <maven.compiler.source>1.8</maven.compiler.source>
        <!--    运行程序使用的jdk版本-->
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>


</project>

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>
    <properties resource="jdbc.properties"/>
    <typeAliases>
        <package name="com.powernode.mybatis.pojo"/>
    </typeAliases>
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="com.powernode.mybatis.mapper"/>
    </mappers>
</configuration>

jdbc.properties

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/powernode
jdbc.username=root
jdbc.password=lzl

logback.xml

<?xml version="1.0" encoding="UTF-8"?>

<configuration debug="false">
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>[%thread] %-5level %logger{50} - %msg%n</pattern>
        </encoder>
    </appender>

    <!--mybatis log configure-->
    <logger name="com.apache.ibatis" level="TRACE"/>
    <logger name="java.sql.Connection" level="DEBUG"/>
    <logger name="java.sql.Statement" level="DEBUG"/>
    <logger name="java.sql.PreparedStatement" level="DEBUG"/>

    <!-- 日志输出级别,logback日志级别包括五个:TRACE < DEBUG < INFO < WARN < ERROR -->
    <root level="DEBUG">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>

</configuration>