MyBatis学习笔记

MyBatis学习笔记

框架概述

常用结构

三层架构

三层框架包含三层

  1. 界面层 - User Interface Layer
  2. 业务逻辑层 - Business Logic Layer
  3. 数据访问层 - Data Access Layer

界面层

主要功能是接收用户的数据,显示请求的处理结果,使用 web 页面实现与用户的交互。手机 app也是表示层,用户在 app中操作,业务逻辑在服务器端处理

业务逻辑层

接收表示传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据

数据访问层

与数据库打交道,主要实现对数据的增删改查,将存储在数据库中的数据提交给业务层,同时将业务层的数据保存到数据库

三层的处理请求的交互

用户 -> 界面层 -> 业务逻辑层 -> 数据访问层 -> DB数据库

使用三层结构的好处

  • 结构清晰、耦合度低、各层次分工明确
  • 可维护性高,可扩展性高
  • 有利于标准化编程
  • 开发人员可以仅关注整个结构中的某一层的功能实现
  • 有利于各层逻辑的复用

常用框架

MyBatis框架

  1. MyBatis是一个优秀的基于 java 的持久层框架,内部封装了 JDBC
  2. 开发者只需要关注 SQL 语句本身为不需要处理加载驱动、创建链接、创建Statement等繁杂的过程。
  3. MyBatis通过 xml或注解两种方式将要执行的各种 SQL语句配置起来,
  4. 通过 java对象和 SQL的动态参数进行映射生成最终执行的 SQL语句
  5. 最后由 MyBatis框架执行 SQL并将结果映射为 java对象并返回

Spring框架

  1. Spring是为了解决软件开发的复杂性而创建的,使用基本的 JavaBean来完成以前非常复杂的企业级开发
  2. 解决了业务对象,功能模块之间的耦合性问题,可在 javaweb,javase等多种场景下使用
  3. Spring是一个轻量级的 控制反转(IoC) 和 面向切面(AOP) 的容器

SpringMVC框架

  1. SpringMVC是SpringFrameWork3.0版本加入的一个模块,提供了 Spring 框架构建 Web 应用程序的能力
  2. 可以在 Spring框架提供的SpringMVC模块中实现 web应用开发

框架解释

框架定义

框架是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法,也可以被认为是应用开发者定制的应用骨架、模板

简单来说,框架就是半成品软件,就是一组组件,供开发者完成自己的系统

总结:框架是安全的、可复用的、不断升级的开发模板

框架解决的问题

框架要解决的最重要的一个问题就是技术整合,在 J2EE 的框架中,有着各种各样的技术,不同的系统使用不同的技术解决问题。因此需要从 J2EE中选择不同的技术,而技术自身的复杂性又导致了更大的风险。

企业在开发软件项目时,主要目的是解决业务问题,既要求企业负责技术本身,又要求解决业务问题。这是大多数企业不能完成的,而框架把相关的技术融合到了一起,使得企业开发可以集中在业务领域方面

JDBC

前情回顾

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

JDBC的缺陷

  • 代码比较多,开发效率低
  • 需要关注 Connection, Statement, ReslutSet对象的创建和销毁
  • 对 ResultSet查询的结果,需要自己封装
  • 重复的代码比较多
  • 业务代码和数据库操作混在一起

MyBatis概述

MyBatis再次介绍

  1. MyBatis 本是 Apache 的一个开源项目 iBatis,2010年这个项目由 Apache Software Foundation迁移到了 Google Code,并改名为 MyBatis,2013年迁移到 GitHub
  2. iBatis 一词来源于 “Internet”和 “Abatis”的组合,是一个基于 Java 的持久层框架

能解决的问题

  1. 解决使用JDBC的复杂性,不用编写重复的创建Connection, Statement语句
  2. 不用编写关闭资源代码,可以直接使用 Java代码,表示结果数据
  3. 开发者专注于 SQL 的处理

快速入门

案例入门

1)下载MyBatis

链接地址:https://github.com/mybatis/mybatis-3/releases

2)创建数据库和表

CREATE TABLE `student` (
 `id` int(11) NOT NULL ,
 `name` varchar(255) DEFAULT NULL,
 `email` varchar(255) DEFAULT NULL,
 `age` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3)创建 maven工程

<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.5.1</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.9</version>
    </dependency>
</dependencies>
<build>
    <resources>
        <resource>
            <directory>src/main/java</directory><!--所在的目录-->
            <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

4)编写实体类

package com.kuang.domain;
public class Student() {
    // 属性名和列名一样
    private Integer id;
    private String name;
    private String email;
    private Integer age;
    // set, get, toString方法
}

5)编写 Dao接口 StudentDao

public interface StudentDao {
    List<Student> selectStudnets();
}

6)创建映射文件 StudentDao.xml

要求:

  • 在dao包中创建映射文件
  • 映射文件名和接口名一样
<?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.bjpowernode.dao.StudentDao">
    <!--
 <select>: 查询数据, 标签中必须是 select 语句
 id: sql 语句的自定义名称,推荐使用 dao 接口中方法名称,
 使用名称表示要执行的 sql 语句
 resultType: 查询语句的返回结果数据类型,使用全限定类名
 -->
    <select id="selectStudents" resultType="com.bjpowernode.domain.Student">
        <!--要执行的 sql 语句-->
        select id,name,email,age from student
    </select>
</mapper>

7)创建主配置文件

在 src/main 下创建 resources 目录,设置 resources 目录为 resources root并在该目录下出创建主配置文件: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">
<configuration>
    <!--配置 mybatis 环境-->
    <environments default="mysql">
        <!--id:数据源的名称-->
        <environment id="mysql">
            <!--配置事务类型:使用 JDBC 事务(使用 Connection 的提交和回滚)-->
            <transactionManager type="JDBC"/>
            <!--
			数据源 dataSource:创建数据库 Connection 对象
             type: POOLED 使用数据库的连接池
             -->
            <dataSource type="POOLED">
                <!--连接数据库的四个要素-->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--告诉 mybatis 要执行的 sql 语句的位置-->
        <mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
    </mappers>
</configuration>

8)创建测试类

@Test
public void testStart() throws IOException {
    //1.mybatis 主配置文件
    String config = "mybatis-config.xml";
    //2.读取配置文件
    InputStream in = Resources.getResourceAsStream(config);
    //3.创建 SqlSessionFactory 对象,目的是获取 SqlSession
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
    //4.获取 SqlSession,SqlSession 能执行 sql 语句
    SqlSession session = factory.openSession();
    //5.执行 SqlSession 的 selectList()
    List<Student> studentList = session.selectList("com.bjpowernode.dao.StudentDao.selectStudents");
    //6.循环输出查询结果
    studentList.forEach( student -> System.out.println(student));
    //7.关闭 SqlSession,释放资源
    session.close();
}

9)配置日志功能

在 mybatis.xml文件中下如如下配置

<settings> <setting name="logImpl" value="STDOUT_LOGGING" /></settings>

CRUD

Insert

1)在 StudentDao 接口中增加方法

int insertStudent(Student studnet);

2)StudentDao.xml 中加入 SQL语句

<insert id="insertStudent">    insert into student(id,name,email,age)    values(#{id},#{name},#{email},#{age})</insert>

3)增加测试方法

@Testpublic void testInsert() throws IOException {    //1.mybatis 主配置文件    String config = "mybatis-config.xml";    //2.读取配置文件    InputStream in = Resources.getResourceAsStream(config);    //3.创建 SqlSessionFactory 对象    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);    //4.获取 SqlSession    SqlSession session = factory.openSession();    //5.创建保存数据的对象    Student student = new Student();    student.setId(1005);    student.setName("张丽");    student.setEmail("zhangli@163.com");    student.setAge(20);    //6.执行插入 insert    int rows = session.insert(        "com.bjpowernode.dao.StudentDao.insertStudent",student);    //7.提交事务    session.commit();    System.out.println("增加记录的行数:"+rows);    //8.关闭 SqlSession    session.close();}

Update

1)StudentDao接口中增加方法

int updateStudent(Student student);

2)在 StudentDao.xml 中增加 SQL语句

<update id="updateStudent">    update student set age = #{age} where id=#{id}</update>

3)增加测试方法

@Testpublic void testUpdate() throws IOException {    //1.mybatis 主配置文件    String config = "mybatis-config.xml";    //2.读取配置文件    InputStream in = Resources.getResourceAsStream(config);    //3.创建 SqlSessionFactory 对象    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);    //4.获取 SqlSession    SqlSession session = factory.openSession();    //5.创建保存数据的对象    Student student = new Student();    student.setId(1005);//要修改的 id    student.setAge(30); //要修改的年龄值    //6.执行更新 update    int rows = session.update(        "com.bjpowernode.dao.StudentDao.updateStudent",student);    //7.提交事务    session.commit();    System.out.println("修改记录的行数:"+rows);    //8.关闭 SqlSession    session.close();}

Delete

1)StudentDao接口中增加方法

int deleteStudent(int id);

2)StudentDao.xml中增加 SQL语句

<delete id="deleteStudent">    delete from student where id=#{studentId}</delete>

3)增加测试方法

@Testpublic void testDelete() throws IOException {    //1.mybatis 主配置文件    String config = "mybatis-config.xml";    //2.读取配置文件    InputStream in = Resources.getResourceAsStream(config);    //3.创建 SqlSessionFactory 对象    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);    //4.获取 SqlSession    SqlSession session = factory.openSession();    //5.删除的 id    int id = 1001;    //6.执行删除 delete    int rows = session.delete(        "com.bjpowernode.dao.StudentDao.deleteStudent",id);    //7.提交事务    session.commit();    System.out.println("修改记录的行数:"+rows);    //8.关闭 SqlSession    session.close();}

对象分析

Resources类

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

SqlSessionFactoryBuilder类

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.bjpowernode.common;/*** <p>Description: 实体类 </p>* <p>Company: http://www.bjpowernode.com*/public class MyBatisUtil {    //定义 SqlSessionFactory    private static SqlSessionFactory factory = null;    static {        //使用 静态块 创建一次 SqlSessionFactory        try{            String config = "mybatis-config.xml";            //读取配置文件            InputStream in = Resources.getResourceAsStream(config);            //创建 SqlSessionFactory 对象            factory = new SqlSessionFactoryBuilder().build(in);        }catch (Exception e){            factory = null;            e.printStackTrace();        }    }    /* 获取 SqlSession 对象 */    public static SqlSession getSqlSession(){        SqlSession session = null;        if( factory != null){            session = factory.openSession();        }        return session;    }}

使用 MyBatisUtil类

@Testpublic void testUtils() throws IOException {    SqlSession session = MyBatisUtil.getSqlSession();    List<Student> studentList = session.selectList(        "com.bjpowernode.dao.StudentDao.selectStudents");    studentList.forEach( student -> System.out.println(student));    session.close();}

Dao开发

1)创建 Dao接口实现类并实现其方法

public class StudentDaoImpl implements StudentDao {    public List<Student> selectStudents() {        SqlSession session = MyBatisUtil.getSqlSession();        List<Student> studentList = session.selectList("com.bjpowernode.dao.StudentDao.selectStudents");        session.close();        return studentList;    }        public int insertStudent(Student student) {        SqlSession session = MyBatisUtil.getSqlSession();        int nums = session.insert("com.bjpowernode.dao.StudentDao.insertStudent",student);        session.commit();        session.close();        return nums;    }            public int updateStudent(Student student) {        SqlSession session = MyBatisUtil.getSqlSession();        int nums = session.insert("com.bjpowernode.dao.StudentDao.updateStudent",student);        session.commit();        session.close();        return nums;    }            public int deleteStudent(int id) {        SqlSession session = MyBatisUtil.getSqlSession();        int nums = session.insert("com.bjpowernode.dao.StudentDao.deleteStudent",1006);        session.commit();        session.close();        return nums;    }}

2)创建测试类并测试方法

public class MyBatisTest {        StudentDao studentDao = new StudentDaoImpl();        @Test    public void testSelect() throws IOException {        final List<Student> studentList = studentDao.selectStudents();        studentList.forEach( stu -> System.out.println(stu));    }    @Test    public void testInsert() throws IOException {        Student student = new Student();        student.setId(1006);        student.setName("林浩");        student.setEmail("linhao@163.com");        student.setAge(26);        int nums = studentDao.insertStudent(student);        System.out.println("使用 Dao 添加数据:"+nums);    }    @Test    public void testUpdate() throws IOException {        Student student = new Student();        student.setId(1006);        student.setAge(28);        int nums = studentDao.updateStudent(student);        System.out.println("使用 Dao 修改数据:"+nums);    }    @Test    public void testDelete() throws IOException {        int nums = studentDao.deleteStudent(1006);        System.out.println("使用 Dao 修改数据:"+nums);    }}

传统 Dao 开发方式的分析

  • Dao的实现类并没有干什么实质性的工作,仅仅是通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id的SQL语句,真正对DB进行的操作是由框架通过 mapper中的 SQL完成的
  • 所以MyBatis框架就抛开了 Dao的实现类,直接定位到了映射文件 mapper中的相应的 SQL语句,称为 Mapper的动态代理方式
  • Mapper动态代理方式无需程序员实现 Dao接口,接口是由 MyBatis结合映射文件自动生成的动态代理实现的

Dao代理

代理实现CRUD

1)通过 getMapper() 获取代理对象

只需要调用 SqlSession 的 getMapper()方法,即可获取指定接口的实现类对象。该方法的参数为指定 Dao接口类的 class值

// 常规方式SqlSession session = factory.openSession();StudentDao dao = session.getMapper(StudentDao.class);// 使用工具类StudentDao dao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class);

2)使用 Dao代理对象方法执行 SQL语句

// select 方法:@Testpublic void testSelect() throws IOException {    final List<Student> studentList = studentDao.selectStudents();    studentList.forEach( stu -> System.out.println(stu));}// insert 方法:@Testpublic void testInsert() throws IOException {    Student student = new Student();    student.setId(1006);    student.setName("林浩");    student.setEmail("linhao@163.com");    student.setAge(26);    int nums = studentDao.insertStudent(student);    System.out.println("使用 Dao 添加数据:"+nums);}// update 方法@Testpublic void testUpdate() throws IOException {    Student student = new Student();    student.setId(1006);    student.setAge(28);    int nums = studentDao.updateStudent(student);    System.out.println("使用 Dao 修改数据:"+nums);}// delete 方法@Testpublic void testDelete() throws IOException {    int nums = studentDao.deleteStudent(1006);    System.out.println("使用 Dao 修改数据:"+nums);}

原理

动态代理

深入理解参数

parameterType

  • parameterType:接口中方法参数的类型,类型的完全限定名或别名。
  • 这个属性是可选的,因为MyBatis可以推断出具体传入语句的参数,默认值为未设置(unset)
  • 接口中方法的参数从 java 代码传入到 mapper 文件的 sql 语句
  • <select>, <insert>, <update>, <delete>都可以使用 parameterType 指定类型
<delete id="deleteStudent" parameterType="int">
    delete from student where id=#{studentId}
</delete>
<!-- 等同于-->
<delete id="deleteStudent" parameterType="java.lang.Integer">
    delete from student where id=#{studentId}
</delete>

只有一个简单参数情况

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

// 接口方法
Student selectById(int id);
<!-- mapper文件-->
<select id="selectById" resultType="com.bjpowernode.domain.Student">
	select id, name, email, age
    from student
    where id=#{studentId}
</select>
<!-- 
	#{studentId},studentId是自定义的变量名称,和方法参数名无关 
-->
// 测试方法
@Test
public void testSelectById() {
    // 1个参数的情况
    Student stu = studentDao.selectById(1005);
    System.out.println("查询id是1005的学生:" + stu);
}

多个参数 - 使用 @Param

当 Dao接口方法中有多个参数时,需要通过名称使用参数。在方法形参前面加入 @Param(“自定义参数名”), mapper文件使用 #{自定义参数名}

// 接口方法List<Studnet> selectMultiParam(@Param("personName") String name, @Param("ersonAge") int age);
<!-- mapper文件--><select id="selectMultiParam" resultType="com.bjpowernode.domain.Student">	select id, name, email, age    from studnet    where name = #{personName} or age = #{personAge}</select>
// 测试方法@Testpublic void testMultiParam() {   List<Studnet> stuList = studentDao.selectMultiParam("李四", 20);    stuList.forEach(stu -> System.out.println(stu))l}

多个参数 - 使用对象

使用 java对象传递参数,java的属性值就是 SQL需要的参数值。每一个属性就是一个参数

语法格式:#{property, javaType=java中的数据类型名, jdbcType=表中的数据类型名}

  • javaType, jdbcType的类型 MyBatis可以自动检测出来,一般不需要设置,所以常用格式也就成了 #{property}

// 创建保存参数值的对象public class QueryParam {    private String quertName;    private int queryAge;    // set, get方法}
// 接口方法List<Student> selectMultiObject(QueryParam queryParam);
<!-- mapper文件 --><select id="selectMultiObject" resultType="studnet">	select id, name, email, age     from student    where name=#{queryName} or age=#{queryAge}</select><!-- 或者 --><select id="selectMultiObject" resultType="studnet">	select id, name, email, age     from student    where name=#{queryName, javaType=string, jdbcType=VARCHAR} or age=#{queryAge, javaType=int, jdbcType=INTEGER}</select>
// 测试方法@Testpublic void selectMultiObject(){    QueryParam qp = new QueryParam();    qp.setQueryName("李力");    qp.setQueryAge(20);    List<Student> stuList = studentDao.selectMultiObject(qp);    stuList.forEach( stu -> System.out.println(stu));}

多个参数 - 按位置

参数位置从 0 开始,引用参数语法 #{arg位置},第一个参数是 #{arg0}, 第二个参数是 #{arg1}

注意:mybatis-3.3版本及之前使用 #{0}, #{1}方式,mybatis-3.4版本及之后使用 #{arg0}方式

// 接口方法List<Student> selectByNameAndAge(String name, int age);
<!-- mapper文件 --><select id="selectByNameAndAge" resultType="studnet">	select id, name, email, age     from student    where name=#{arg0} or age=#{arg1}</select>
// 测试方法@Testpublic void selectMultiObject(){    // 按位置参数   List<Student> stuList = studentDao.selectByNameAndAge("李四", 20);    stuList.forEach(stu -> System.out.println(stu));}

多个参数 - 使用 Map

Map集合可以存储多个值,使用 Map 可以向 mapper文件一次传入多个参数。

Map集合使用String类型的 key,Object类型的值存储参数。mapper文件使用 #{key}引用参数

 // 接口方法List<Student> selectMultiMap(Map<String, Object> map);
<!-- mapper文件 --><select id="selectMultiMap" resultType="student">	select id, name, email, age    from student    where name=#{myname} or age=#{myage}</select>
// 测试方法@Testpublic void testSelectMultiMap() {    Map<String, Object> data = new HashMap<>;    data.put("myname", "李四");    data.put("myage", 20);    List<Student>stuList = studentDao.selectMultiMap(data);    stuList.forEach(stu -> System.out.println(stu));}

# 和 $ 的比较

#:占位符

告诉 MyBatis 使用实际的参数值代替,并使用 PreparedStatement对象执行 SQL语句,#{…}代替了 SQL语句中的“?”。

这样做更安全、迅速,通常也是首选的做法

<!-- mapper文件 --><select id="selectById" resultType="studnet">    select id, name, email, age    from student    where id=#{studentId}</select>
// 转为 MyBatis执行是:String sql = "select id, name, email, age from student where id=?";PreparedStatement ps = conn.prepareStatement(sql);ps.setInt(1, 1005);/*	解释:	where id=?	就是 where id=#{studentId}	ps.setInt(1, 1005),1005会替换掉 #{studentId}*/

$:字符串替换

告诉 MyBaits使用 $ 包含的 “字符串”替换所在位置。使用 Statement 把 SQL 语句和 ${}的内容连接起来。

主要用在替换表名、列名,不同列排序等操作

例1:分别使用 id,email 查询 Student

// 接口方法Student findById(int id);Studnet findByEmail(String email);
<!-- mapper文件 --><select id="findById" resultType="com.bjpowernode.domain.Student">    select * from student where id=#{studentId}</select><select id="findByEmail" resultType="com.bjpowernode.domain.Student">    select * from student where email=#{stuentEmail}</select>
// 测试方法@Testpublic void testFindStuent(){    Student student1 = studentDao.findById(1002);    System.out.println("findById:"+student1);    Student student2 = studentDao.findByEmail("zhou@126.net");    System.out.println("findByEmail:"+student2);}

例2:通用方法,使用不同列作为查询条件

// 接口方法Studnet findByDifferField(@Param("col")String colunName, @Param("cval")Object value);
<!-- mapper文件 --><select id="findByDiffField" resultType="com.bjpowernode.domain.Student"> select * from student where ${col} = #{cval}</select>
// 测试方法@Testpublic void testFindDiffField(){    Student student1 = studentDao.findByDiffField("id",1002);    System.out.println("按 id 列查询:"+student1);    Student student2 = studentDao.findByDiffField("email","zhou@126.net");    System.out.println("按 email 列查询:"+student2);}

封装输出结果

resultType

执行 SQL 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。

注意:如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。

resultType 和 resultMap不能同时使用

1)简单类型

// 接口方法int countStudent();
<!-- mapper文件 --><select id="countStudent" resultType="int">	select count(*)     from student</select>
// 测试方法@Testpublic void testReturnInt() {    int count = studentDao.countStudent();    System.out.println("学生总人数:" + count);}

2)对象类型

// 接口方法Student selectById(int id);
<!-- mapper文件 --><select id="selectById" resultType="student">	select id, name, email, age    from student    where id=#{studentId}</select>

框架的处理:使用构造方法创建对象,调用 setXxx()给属性赋值

Student stu = new Student();

注意:Dao接口方法返回是集合类型,需要指定集合中的类型,不是集合本身

3)Map

SQL的查询结果作为 Map的 Key和 value,推荐使用 Map<Object, Object>

注意:Map作为接口返回值,SQL语句的查询结果最多只能有一条记录

// 接口方法Map<Object, Object> selectReturnMap(int id);
<!-- mapper文件 --><select id="selectReturnMap" resultType="hashMap">	select name,email     from student     where id = #{studentId}</select>
// 测试方法@Testpublic void testReturnMap(){    Map<Object,Object> retMap = studentDao.selectReturnMap(1002);    System.out.println("查询结果是 Map:"+retMap);}

resultMap

resultMap 可以自定义 SQL的结果和 Java对象属性的映射关系。更灵活的把列值赋值给指定属性。

常用在列名和java对象属性名不一样的情况

使用方式

  1. 先定义 resultMap,指定列名和属性的对应关系
  2. 在 <select>中把 resultType 替换为 resultMap
// 接口方法List<Student> selectUseResultMap(QueryParam param);
<!-- mapper文件 --><!-- 创建 resultMap id:自定义的唯一名称,在<select>使用 type:期望转为的 java 对象的全限定名称或别名--><resultMap id="studentMap" type="com.bjpowernode.domain.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 id,name,email,age from student where name=#{queryName} or    age=#{queryAge}</select>
// 测试方法@Testpublic void testSelectUseResultMap(){    QueryParam param = new QueryParam();    param.setQueryName("李力");    param.setQueryAge(20);    List<Student> stuList = studentDao.selectUseResultMap(param);    stuList.forEach( stu -> System.out.println(stu));}

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

1)使用列别名和 <resultType>

// 创建新的实体类public class PrimaryStudent {    private Integer stuId;    private String stuName;    private Integer stuAge;    // set , get 方法}
// 接口方法List<PrimaryStudent> selectUseFieldAlias(QueryParam param);
<!-- mapper文件 --><select id="selectUseFieldAlias"        resultType="com.bjpowernode.domain.PrimaryStudent">    select id as stuId, name as stuName,age as stuAge    from student where name=#{queryName} or age=#{queryAge}</select>
// 测试方法@Testpublic void testSelectUseFieldAlias(){    QueryParam param = new QueryParam();    param.setQueryName("李力");    param.setQueryAge(20);    List<PrimaryStudent> stuList;    stuList = studentDao.selectUseFieldAlias(param);    stuList.forEach( stu -> System.out.println(stu));}

2)使用 <resultMap>

// 接口方法List<PrimaryStudent> selectUseDiffResultMap(QueryParam param);
<!-- mapper文件 --><!-- 创建 resultMap id:自定义的唯一名称,在<select>使用 type:期望转为的 java 对象的全限定名称或别名--><resultMap id="primaryStudentMap"           type="com.bjpowernode.domain.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 id,name,email,age from student    where name=#{queryName} or age=#{queryAge}</select>
// 测试方法@Testpublic void testSelectUseDiffResultMap(){    QueryParam param = new QueryParam();    param.setQueryName("李力");    param.setQueryAge(20);    List<PrimaryStudent> stuList;    stuList = studentDao.selectUseDiffResultMap(param);    stuList.forEach( stu -> System.out.println(stu));}

模糊查询

模糊查询的实现有两种方式:

  1. 在 java代码中给查询数据加上 “%”
  2. 在 mapper文件 SQL语句的条件位置加上 “%”

1)例1:java代码中提供 “%”

// 接口方法List<Student> selectLikeFirst(String name);
<!-- mapper文件 --><select id="selectLikeFirst" resultType="com.bjpowernode.domain.Student">    select id,name,email,age     from student    where name like #{studentName}</select>
// 测试方法:@Testpublic void testSelectLikeOne(){    String name="%力%";    List<Student> stuList = studentDao.selectLikeFirst(name);    stuList.forEach( stu -> System.out.println(stu));}

2)mapper文件中使用 like

// 接口方法List<Student> selectLikeSecond(String name);
<!-- mapper文件 --><select id="selectLikeSecond" resultType="com.bjpowernode.domain.Student">    select id,name,email,age     from student    where name like "%" #{studentName} "%"</select>
// 测试方法@Testpublic void testSelectLikeSecond(){    String name="力";    List<Student> stuList = studentDao.selectLikeSecond(name);    stuList.forEach( stu -> System.out.println(stu));}

动态SQL

动态 SQL,指的是通过 MyBatis 提供的各种标签对条件作出判断以实现动态拼接 SQL语句的效果。

这里的条件判断使用 OGNL表达式,常用的动态 SQL标签有 <if>, <where>, <choose>, <foreach>

动态 SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据用户提交的条件进行查询。提交的查询条件不同,执行的 SQL语句不同。

注意:在 mapper 的动态 SQL中若出现大于号、小于号、大于等于号等符号,最好将其转换为实体符号。否则,XML可能会解析出错

符号 意思 代码
< 小于 &lt;
> 大于 &gt;
>= 大于等于 &gt;=
<= 小于等于 &lt;=

<if>标签

对于该标签的执行,当 test的值为 true时,会将其包含的 SQL片段拼接到其所在的 SQL语句中。

语法:<if test=“条件”> SQL 语句 </if>

// 接口方法List<Student> selectStudentIf(Student student);
<!-- mapper文件 --><select id="selectStudentIf" resultType="com.bjpowernode.domain.Student">    select id,name,email,age from student    where 1=1    <if test="name != null and name !='' ">        and name = #{name}    </if>    <if test="age > 0 ">        and age &gt; #{age}    </if></select>
// 测试方法@Testpublic void testSelect() throws IOException {    Student param = new Student();    param.setName("李力");    param.setAge(18);    List<Student> studentList = studentDao.selectStudentIf(param);    studentList.forEach( stu -> System.out.println(stu));}

<where>标签

使用 <where>标签能使 SQL语句在存在查询条件时,自动添加上 where子句;没有查询条件时,不会添加 where子句

第一个 <if>标签中 可以包含 and,也可以不包含,系统会将多余的 and去掉。但其它<if>中必须写上 and,否则拼接会出错

语法:<where>其他动态 SQL<where>

// 接口方法List<Student> selectStudentWhere(Student student);
<!-- mapper文件 --><select id="selectStudentWhere" resultType="com.bjpowernode.domain.Student">    select id,name,email,age from student    <where>        <if test="name != null and name !='' ">            and name = #{name}        </if>        <if test="age > 0 ">            and age &gt; #{age}        </if>    </where></select>
// 测试方法@Testpublic void testSelectWhere() throws IOException {    Student param = new Student();    param.setName("李力");    param.setAge(18);    List<Student> studentList = studentDao.selectStudentWhere(param);    studentList.forEach( stu -> System.out.println(stu));}

<foreach>标签

<foreach>标签用于实现对于数组和集合的遍历

  • collection 表示要遍历的集合类型,list, array等
  • open, close, separator 为遍历内容的 SQL拼接

语法:<foreach collection="集合类型”, open="开始的字符”, close="结束的字符”, item="集合中的成员”, separator="分隔符">#{item的值}</foreach>

遍历 List<简单类型>

表达式中的 List使用 list表示,其大小使用 list.size表示

// 接口方法List<Student> selectStudentForList(List<Integer> idList);
<!-- mapper文件 --><select id="selectStudentForList"        resultType="com.bjpowernode.domain.Student">    select id,name,email,age from student    <if test="list !=null and list.size > 0 ">        where id in        <foreach collection="list" open="(" close=")"                 item="stuid" separator=",">            #{stuid}        </foreach>    </if></select>
// 测试方法@Testpublic void testSelectForList() {    List<Integer> list = new ArrayList<>();    list.add(1002);    list.add(1005);    list.add(1006);    List<Student> studentList = studentDao.selectStudentForList(list);    studentList.forEach( stu -> System.out.println(stu));}

遍历 List<对象类型>

// 接口方法List<Student> selectStudentForList2(List<Student> stuList);
<!-- mapper文件 --><select id="selectStudentForList2"        resultType="com.bjpowernode.domain.Student">    select id,name,email,age from student    <if test="list !=null and list.size > 0 ">        where id in        <foreach collection="list" open="(" close=")"                 item="stuobject" separator=",">            #{stuobject.id}        </foreach>    </if></select>
//  测试方法@Testpublic void testSelectForList2() {    List<Student> list = new ArrayList<>();    Student s1 = new Student();    s1.setId(1002);    list.add(s1);    s1 = new Student();    s1.setId(1005);    list.add(s1);    List<Student> studentList = studentDao.selectStudentForList2(list);    studentList.forEach( stu -> System.out.println(stu));}

<sql>标签

<sql>标签用于定义 SQL片段,以便其他 SQL标签的复用。而其他标签内部引入该 SQL片段时,需要通过 <include>子标签

<sql>标签可以定义 SQL语句中的任何部分,所以 <include>子标签可以放在动态 SQL的任何位置

// 接口方法List<Student> selectStudentSqlFragment(List<Student> stuList);
<!-- mapper文件 --><sql id="studentSql">    select id,name,email,age from student</sql><select id="selectStudentSqlFragment"        resultType="com.bjpowernode.domain.Student">    <!-- 引用 sql 片段 -->    <include refid="studentSql"/>    <if test="list !=null and list.size > 0 ">        where id in        <foreach collection="list" open="(" close=")"                 item="stuobject" separator=",">            #{stuobject.id}        </foreach>    </if></select>
// 测试方法@Testpublic void testSelectSqlFragment() {    List<Student> list = new ArrayList<>();    Student s1 = new Student();    s1.setId(1002);    list.add(s1);    s1 = new Student();    s1.setId(1005);    list.add(s1);    List<Student> studentList = studentDao.selectStudentSqlFragment(list);    studentList.forEach( stu -> System.out.println(stu));}

配置文件

主配置文件

主配置文件即核心配置文件,mybatis.xml

特点:

  1. 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">
    
  2. 根元素

    <configuration></configuration>
    
  3. 包含内容:定义别名、数据源、mapper文件

dataSource标签

dataSource类型

MyBatis 中访问数据库可以通过连接池技术,通过 <dataSource type="pooled">来实现

  • UNPOOLED:不使用连接池的数据源
  • POOLED:使用连接池的数据源
  • JNDI:使用 JNDI 实现的数据源

其中,UNPOOLED、POOLED 数据源实现了 javax.sql.DataSource 接口,JNDI 和前面两个实现方式不同。

dataSource配置

在 MyBatis.xml 文件中,如下配置 dataSource

<dataSource type="POOLED">    <!--连接数据库的四个要素-->    <property name="driver" value="com.mysql.jdbc.Driver"/>    <property name="url" value="jdbc:mysql://localhost:3306/ssm?charset=utf-8"/>    <property name="username" value="root"/>    <property name="password" value="123456"/></dataSource>

MyBatis.xml 在初始化时,会根据 <dataSource>标签中的 type属性来创建相应类型的数据源

  • type = POOLED:会创建 PooledDataSource实例
  • type = UNPOOLED:会创建 UnPooledDataSource实例
  • type = JNDI:会从 JNDI服务上查找 DataSource实例,然后返回

事务处理

1)默认手动提交事务

MyBatis框架是对 JDBC的封装,所以 MyBatis框架的事务控制方式也是使用 JDBC的 Connection对象的 commit(), rollback()

Connection对象的 setAutoCommit() 方法来设置事务的提交方式

可使用 <transactionManager type=“xxx”>标签来指定事务管理器

  • type = JDBC:使用 JDBC的事务管理机制,默认情况下自动提交功能关闭了,程序中需要显式地对事务进行提交和回滚
  • type = MANAGED:由容器来管理事务的整个生命周期

2)自动提交事务

设置自动提交的方法为 SqlSessionFactory对象的 openSession()方法

  • SqlSession openSession(boolean autoCommit);

参数为 true时,启用自动提交

空参或参数为 false时,需要手动提交

数据库属性配置

为了方便对数据库连接的管理,DB连接四要素数据一般都是存放在一个专门的属性文件中的

MyBatis主配置文件需要从这个属性文件中读取这些数据

1)在 classpath路径下创建 properties文件

在 resources目录下创建 jdbc.properties文件,名称可自定义

jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/ssm?charset=urf-8jdbc.username=rootjdbc.password=zhao

2)在核心配置文件中使用<properties>标签

修改著配置文件,在文件的开始位置处加入:

<?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"/></configuration>

3)使用 key指定值

<dataSource type="POOLED">    <!--  使用properties文件,语法:${key} -->    <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>

类型别名

MyBatis支持默认别名和自定义别名,可在 <select resultType="别名">等标签中使用

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

映射器

1)<mapper resource=“”>

使用相对于类路径的资源,从 classpath路径查找文件

<mappers>    <mapper resource="com/kuang/dao/StudentDao.xml" /></mappers>

2)<package name=“”/>

指定包名下的所有 Dao接口,该方法只有在 Dao接口名称和 mapper映射文件同名并且在同一个目录的情况下才可以使用

<mappers>    <package name="com.kuang.dao"/></mappers>

扩展内容

分页工具

PageHelper是MyBatis的通用分页插件

1)pom依赖

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

2)plugin配置

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor" />
</plugins>

3)使用PageHelper对象

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

@Test
public void testSelect() throws IOException {
    //获取第 1 页,3 条内容
    PageHelper.startPage(1,3);
    List<Student> studentList = studentDao.selectStudents();
    studentList.forEach( stu -> System.out.println(stu));
}
posted @ 2021-02-17 21:39  小么VinVin  阅读(102)  评论(0编辑  收藏  举报