【01】SSM的知识与应用之Mybatis

1. MyBatis 持久层框架

1.1概念

MyBatis的前身就是iBatis,iBatis本是apache的一个开源项目,2010年5月这个项目由apahce sofeware foundation 迁移到了google code,并且改名为MyBatis。
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。
优点:
(1)简化JDBC的开发。
(2)能够更好的完成ORM(对象关系映射),是指类和表的关系,是属性和字段值的关系。

1.2 内部组件结构图

1.3 核心组件

  • 核心配置文件:mybatis-config.xml,里面写数据源,事务,映射文件
  • 映射文件:XxxMapper.xml ,里面写了大量的SQL语句
  • SqlSessionFactory:是SQL的会话工厂,用来产生很多的会话,对象唯一共享
  • SqlSession:是SQL的会话,用来执行SQL
  • ORM:对象关系的自动映射,把car表里的字段的值查出来,一个一个,给Car类里的属性赋值

2. 入门案例1 --Mybatis的XML文件映射方式

2.1 准备数据库表

create database mybatisdb default character set utf8;
use mybatisdb;
create table user(id int primary key auto_increment,name varchar(100),addr varchar(100),age int);
Insert into user values(null,'hanmeimei','北京',28);
Insert into user values(null,'xiongda','上海',20);
Insert into user values(null,'xiaonger','上海',19);
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `dname` varchar(14) DEFAULT NULL,
  `loc` varchar(13) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES ('1', '呵呵呵', '一区');
INSERT INTO `dept` VALUES ('2', '哈哈哈哈', '二区');
INSERT INTO `dept` VALUES ('3', 'operations', '二区');
INSERT INTO `dept` VALUES ('5', 'java教研部', '大钟寺');
INSERT INTO `dept` VALUES ('10', '开发', '西二旗');
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `ename` varchar(10) DEFAULT NULL,
  `job` varchar(9) DEFAULT NULL,
  `mgr` decimal(4,0) DEFAULT NULL,
  `hiredate` date DEFAULT NULL,
  `sal` decimal(7,2) DEFAULT NULL,
  `comm` decimal(7,2) DEFAULT NULL,
  `deptno` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=510 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES ('100', 'jack', '副总', null, '2002-05-03', '90000.00', null, '1');
INSERT INTO `emp` VALUES ('200', 'tony', '总监', '100', '2015-02-02', '10000.00', '2000.00', '2');
INSERT INTO `emp` VALUES ('300', 'hana', '经理', '200', '2017-02-02', '8000.00', '1000.00', '2');
INSERT INTO `emp` VALUES ('400', 'leo', '员工', '300', '2019-02-22', '3000.00', '200.12', '2');
INSERT INTO `emp` VALUES ('500', 'liu', '员工', '300', '2019-03-19', '3500.00', '200.58', '2');
INSERT INTO `emp` VALUES ('502', '王一博', 'topidol.', '1000', '2021-03-31', '20000.00', '99.00', '88');
INSERT INTO `emp` VALUES ('504', '蔡徐坤', 'rapper', '10', '2021-03-29', '100.00', '1000.00', '100');

2.2 创建Maven工程

2.3 配置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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.tedu</groupId>
    <artifactId>springmybatis-demo2</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>springmybatis-demo2</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--mybatis依赖包-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <!--jdbc依赖包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.3</version>
            </plugin>
        </plugins>
    </build>
</project>

2.4 创建User类

package cn.tedu.pojo;

/**
 * 封装User表里的每个字段值,要求类名与表明对应,属性名与字段名一致
 */
public class User {
    private Integer id;
    private String name;
    private String addr;
    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 getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    public Integer getAge() {
        return age;
    }

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

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

2.5 创建核心配置文件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">

<!-- mybatis框架的核心配置文件 ,配置了数据源、事务、映射文件-->
<configuration>
    <environments default="test">
        <!--配置数据源、事务-->
        <environment id="test">
            <!--配置了事务,使用jdbc提供事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置了数据源,指定了连接数据库的4个参数-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai" />
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--用来引入映射文件-->
    <mappers>
        <mapper resource="UserMapper.xml"></mapper>
    </mappers>

</configuration>

2.6 配置映射文件UserMapper.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">

<!--mybatis的映射文件,用来写大量的SQL;namespace是命名空间,用来作为每个映射文件的唯一标识。-->
<mapper namespace="cn.tedu.dao">
    <select id="getUser" resultType="cn.tedu.pojo.User">
        select * from user
    </select>
</mapper>

2.7 测试

package cn.tedu;

import cn.tedu.pojo.User;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

//测试mybatis
public class TestMybatis {
    @Test
    public void getAll() throws IOException {
        //1.读取核心配置文件
       InputStream in =  Resources.getResourceAsStream("mybatis-config.xml");
        //2.创建会话工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.打开SQL会话
        SqlSession session = factory.openSession();
        //4.执行SQL -- 定位SQL的方式是:namespace的值.id的值
        List<User> list = session.selectList("cn.tedu.dao.getUser");
        //5.遍历list
        for (User u : list){
            System.out.println(u);
        }
    }
}

运行结果:

Mybatis为了提高查询效率,提供了缓存技术(基于SqlSession会话来实现,必须为同一个session)。当第一次查询数据时,缓存中没有,会直接去查询数据库,把数据存入缓存中。第二次查询相同数据时,先去查询缓存,如果有就会直接获取,没有再去查询数据库。

3 入门案例2 --Mybatis的接口映射方式

3.1 概述

在上面的入门案例中,在调用session方法时,都会传入要调用的SQL的namespace+id名称。这种方式很容易出错,因为是String类型的,所以,mybatis提供了一种非常好的设计方式来避免这种问题,即:Mapper接口映射方式。

3.2 创建Maven工程

3.3 配置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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.tedu</groupId>
    <artifactId>springmybatis-demo3</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>springmybatis-demo3</name>
    <description>Demo project for Spring Boot</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--mybatis依赖包-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>
        <!--jdbc依赖包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.48</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.5.3</version>
            </plugin>
        </plugins>
    </build>

</project>

3.4 创建Dept类

package cn.tedu.pojo;

//完成ORM,自动实现把dept表里字段的值   封装给不同属性
public class Dept {
    private Integer id;//描述dept表里的id字段
    private String dname;//描述dept表里的dname字段
    private String loc;//描述dept表里的loc字段

    //set get toString
    public Integer getId() {
        return id;
    }

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

    public String getDname() {
        return dname;
    }

    public void setDname(String dname) {
        this.dname = dname;
    }

    public String getLoc() {
        return loc;
    }

    public void setLoc(String loc) {
        this.loc = loc;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "id=" + id +
                ", dname='" + dname + '\'' +
                ", loc='" + loc + '\'' +
                '}';
    }
}

3.5 创建DeptMapper.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属性用来作为mapper文件的唯一标识,namespace属性的值是接口的全路径
-->
<mapper namespace="cn.tedu.dao.DeptDao">
    <!--select用来标记着这是一个查询的SQL语句
        id属性是SQL的唯一标识
        id属性的值是 接口中对应的方法名
        resultType属性的值是 要把查询结果封装给哪个类的全路径
    -->
    <select id="getAll" resultType="cn.tedu.pojo.Dept">
        select * from dept
    </select>
</mapper>

3.6 创建核心配置文件

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

<!-- mybatis框架的核心配置文件 ,配置了数据源、事务、映射文件-->
<configuration>
    <environments default="test">
        <!--配置数据源、事务-->
        <environment id="test">
            <!--配置了事务,使用jdbc提供事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置了数据源,指定了连接数据库的4个参数-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai" />
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <!--用来引入映射文件-->
    <mappers>
        <mapper resource="DeptMapper.xml"></mapper>
    </mappers>
</configuration>

3.7 测试代码

package cn.tedu;

import cn.tedu.dao.DeptDao;
import cn.tedu.pojo.Dept;
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.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

//测试Mybatis接口映射的方式
public class TestMabatis {

    @Test
    public void getDept() throws IOException {
        //1.读取核心配置文件
        InputStream  in = Resources.getResourceAsStream("mybatis-config.xml");
        //2.常见会话工厂
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        //3.开启会话
        SqlSession session = factory.openSession();
        //4.执行SQL ,执行接口中的抽象方法 --- 参数是接口文件的class对象
        DeptDao dao = session.getMapper(DeptDao.class);
        List<Dept> list =  dao.getAll();
       for (Dept d : list){
           System.out.println(d);
       }
    }
}

运行结果:

4. 解析SQL参数

4.1 获取用户传入的参数

获取用户传入的参数,固定语法:#{传入的参数名}或者${传入的参数名}

 <select id="getDept" resultType="Dept">
        select * from dept where id = #{id} 
    </select>

说明:$与#传值的区别

  • 使用#做参数传值时,使用新对象传递,防止SQL注入,如果字段参数是字符串类型的,会自动拼接双引号,高效安全。
  • 使用$做参数传值时,直接把参数拼在SQL中,存在SQL注入问题,不会为字符串类型的参数拼接双引号,执行时就会产生SQL错误。

4.2 参数类型 paramterType

指定参数类型,通常指定一个对象类型。

4.3 返回值类型

resultType

一般用于查询过程,把结果集转换成指定的对象实例。

resultMap

如果类中的属性名与表中的字段名一致时,可以自动完成ORM映射关系。
如果类中的属性名与表中的字段名不一致时,resultType无法完成ORM映射,需要使用resultMap。

  • 用法:
    把SQL标签的属性resultType改为resultMap,并单独处理属性名与字段名不一致的特殊情况。
  • 问题:

  • 解决方案
 <!--当字段名与属性名不一致时,可用resultMap;id属性:每个resultMap的唯一标识,type属性:需要ORM映射的类的全路径-->
    <resultMap id="UserERM" type="cn.tedu.pojo.UserExtra">
        <!--column描述表中的字段名,propery描述的类中的属性名-->
        <result column="user_id" property="userId"></result>
    </resultMap>
    
    <select id="getUser" resultMap="UserERM">
        select * from user_extra
    </select>

  • 可得正确结果:

起别名

<typeAliases>
	<typeAlias 	type=" "	alias=" "></typeAlias>
</typeAliases>

第一步:在核心配置文件中添加代码。

第二步:修改映射文件。

5. 动态SQL

Mybatis提供使用ognl表达式动态生成sql的功能。

5.1 sql和include 标签

  • sql标签用来提取SQL片段,来提高SQL的复用。
  • include标签用来引用指定的SQL片段。
  <!--动态Sql标签,用来提取共性sql-->
    <sql id="colums">
        id,dname,loc
    </sql>

    <select id="getAll" resultType="Dept">
        select
        <!--引用指定片段-->
        <include refid="colums"></include>
        from dept
    </select>

5.2 if 标签

执行SQL时,可以添加一些判断条件。
if标签用来做判断,满足条件才执行SQL,test属性用来写判断条件

<select id="getDept" resultType="Dept">
        select *
        from dept where
        <if test="id!=null">
            id = #{id}
        </if>
    </select>

5.3 where 标签

去掉条件中可能多余的and或者or;

<select id="find" resultType="Item" parameterType="Item">
SELECT <include refid="cols"/> FROM tb_item
<where> 
	<if test="title != null"> title like #{title} </if>
	<if test="sellPoint != null">and sell_point like #{sellPoint}</if>
</where>
</select>

5.4 set 标签

去掉最后可能多余的逗号:

<update id="update">
UPDATE teachers 
<set>
		<if test="tname != null">tname=#{tname},</if>
		<if test="tsex != null">tsex=#{tsex},</if>
		<if test="tbirthday != null">tbirthday=#{tbirthday},</if>
		<if test="prof != null">prof=#{prof},</if>
		<if test="depart != null">depart=#{depart}</if>
</set>
WHERE tno=#{tno}	
</update>

5.5 foreach 标签

用于in子查询中的多个值的遍历;

List<Dept> getById(Integer[] ids);
List<Dept> ary =  dao.getById(new Integer[]{1, 2, 3});
<select id="getById" resultType="Dept">
        select *
        from dept
        where id in
        <!--获取数组中的数据,collection的值是固定值:array-->
        <foreach collection="array" open="(" close=")" separator="," item="i">
            #{i}
        </foreach>
</select>

6. 小结分析

6.1 JDBC和MyBatis的区别

  • JDBC是java提供了一套专门用于和数据库对接的api,java.sql.*,其规范了如何和数据库进行对接,实现由各数据库厂商进行各自的实现和扩展。学习JDBC重点在学习如何使用其api。

  • MyBatis框架是轻量级封装了JDBC,我们已经看不到这些api,连接connection、语句preparedstatement、结果集ResultSet,而关注的是mybatis框架体系如何去使用,一旦写好,我们关注的是java对象。

6.2 XML和接口方式的区别?

  • MyBatis提供了两种操作数据库的方式,一种是通过xml映射文件,一种是通过java的接口类。按面向对象方式更加推荐接口方式,但如果复杂的多表映射,仍然需要使用xml映射文件的ResultMap方式实现。
  • 接口只是假象,其底层仍然是通过xml实现,好不容易实现了一套方式,怎忍丢掉呢?可以做个测试就知道它底层怎么实现的?把xml中的sql删除,它就玩不转了。

6.3 接口方式怎么找到xml执行的?

SqlSession的getMapper方法找到类,通过反射可以获取到类的全路径(包名.类名),相加后就定位到某个xml的命名空间namespace,在根据调用的方法去找到xml中某个标签的id属性。从而实现价值接口,调用接口的方法而间接找到xml中的标签,通过解析xml获取这个标签的内容,从而获取到sql语句。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值