mybatis的框架(深入)

本文详细介绍了MyBatis框架的使用,包括XML配置和接口方式的SQL执行,以及数据源、会话工厂和会话的创建。讨论了MyBatis的优势,如简化JDBC开发和ORM映射,并展示了如何创建数据库表、配置文件和映射文件。此外,还涵盖了动态SQL标签的使用,如if、where和foreach,以及resultMap的配置,用于处理字段名与属性名不一致的情况。
摘要由CSDN通过智能技术生成

注意:如果使用了yml文件,那么最好不要使用配置文件,里面有一些内容冲突,会报错

mybatis的两种方式:一种是xml文件的方式,一种是借口方式(可以使用xml文件,还可以使用yml方式);

此处 是使用idea进行mybatis框架

mybatis

mybatis的MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。

 优势:

  1. 简化JDBC的开发
  2. 能够更好的完成ORM(对象关系映射)
  3. 还提供了缓存:当第一次查询数据库的时候会执行sql语句到数据库中进行查询,那么在以后的查询就会在缓存中进行查询,不到数据库中进行查询 ;

ORM:封装类的属性和表的列名进行对应(此处相当于jdbc中的set)关系;以及类和表的对应关系 ;

mybatis的内部组件结构图 

数据源:相当于连接数据库封装了密码和用户名和连接数据库

创建对象

回话工厂:就是将读取 的配置文件信息放到工厂里(对象)

回话(sqlsession) : 获取配置文件的sql语句,并执行sql语句;

创建数据表

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');

使用mybatis必须要有两个文件(配置文件,映射文件)

–核心配置文件:mybatis-config.xml,里面写数据源,事务,映射文件
–映射文件:XxxMapper.xml ,里面写了大量的SQL
–SqlSessionFactory:是SQL的会话工厂,用来产生很多的会话,对象唯一共享
–SqlSession:是SQL的会话,用来执行SQL

在这里插入图片描述


 

创建配置文件

<?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">
        <!--在environments可以存放多个库-->
        <!--一个environment带表一个数据库,id不能重复-->
        <environment id="test">
            <!--代表数据事务;此处使用jdbc管理事务-->
            <transactionManager type="JDBC"></transactionManager>
            <!--配置数据源;此处的类型为连接池-->
            <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>
    <!--resource值要引入的映射文件的名字-->
    <mapper resource="UserMapper.xml"></mapper>
    <mapper resource="DeptMapper.xml"></mapper>
</mappers>
</configuration>

在配置文件中引入映射文件

<!-- 用来引入映射文件 ,底层维护了MapperRegistry,用来存各种Mapper文件-->
    <mappers>
        <!--加入了一个新的映射文件-->
        <mapper resource="CarMapper.xml"></mapper>
        <mapper resource="UserMapper.xml"></mapper>
        <mapper resource="DeptMapper.xml"></mapper>
    </mappers>

创建映射文件

<?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.CarMapper">
    <!--select用来标记着这是一个查询的SQL语句
        id属性是SQL的唯一标识
        id属性的值是 接口里对应的方法名
        resultType属性的值是 要把查询结果封装给哪个类的全路径
    -->
    <select id="selectList" resultType="cn.tedu.pojo.Car">
        select * from car
    </select>
</mapper>

创建测试类

package cn.tedu.mybatis;

import cn.tedu.dao.CarMapper;
import cn.tedu.pojo.Car;
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.BeforeEach;
import org.junit.jupiter.api.Test;

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

public class TestCar {
    private SqlSessionFactory factory;
     //提取公共的代码,将公共的代码放到这个方法里
    @BeforeEach //Junit5版本才支持的注解,Junit4版本用@Before
    public void init() throws IOException {
        //1,读取核心配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        //2,创建会话工厂
        factory = new SqlSessionFactoryBuilder().build(in);
    }
    @Test
    public void get(){
        //开启会话
        SqlSession session = factory.openSession();
        //获取了指定的接口
        CarMapper mapper = session.getMapper(CarMapper.class);
        //调用接口里的方法
        List<Car> list = mapper.selectList();
        //遍历打印
        for (Car c : list) {
            System.out.println(c);
        }
    }

}

如果返回的是一个数组则使用下面的代码

List<Car> list = mapper.selectList();
        //遍历打印
        for (Car c : list) {
            System.out.println(c);
        }

如果返回的是某一行的值则使用下面的代码

 User u = session.selectOne("userMapperNS.getById");
        System.out.println(u);

封装一个与表相对应的类

注意:此处的类名为对应 的表名, 属性为对应的列名;

package cn.tedu.pojo;

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 +
                '}';
    }
}

创建测试类是用来测试框架

创建CarMapper 接口

package cn.tedu.dao;
import cn.tedu.pojo.Car;
public interface CarMapper {

    Car getById(Integer id);/**查询id=1的汽车数据*/

}

总结:

在这里插入图片描述

三,解析SQL参数的练习

 <!--按名字查-->
    <select id="getByName" resultType="cn.tedu.pojo.Car">
        select * from car where
        name = #{name}
        /*$和#都可以获取参数,但是 $不会拼接字符串不安全低效,#会自动拼接字符串而且高效安全避免了SQL攻击问题*/
    </select>

接口里的代码

import cn.tedu.pojo.UserExtra;

import java.util.List;

public interface CarDao {

    Car getname(String name);
    }

修改测试的代码

@Test
    public void get(){
        SqlSession session = factory.openSession();
        CarMapper mapper = session.getMapper(CarMapper.class);
        Car c = mapper.getById(2);//按照id查
        System.out.println(c);

        Car c2 = mapper.getByName("bmw");//按照name查
        System.out.println(c2);
    }

–3,标签测试

sql和include标签

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace的值是接口的全路径-->
<mapper namespace="cn.tedu.dao.CarMapper">
    <!--1.  动态SQL标签,用来提取共性的SQL-->
    <sql id="columns">
        id,name,color,price
    </sql>
<select id="getById" resultType="cn.tedu.pojo.Car">
        select
        /*2. 引用了指定的SQL片段*/
         <include refid="columns"></include>
        from car
       
        /*获取用户传入的参数,固定语法::${要获取谁的值}*/
    </select>

获取name的值  if  和 where 标签

 <!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace的值是接口的全路径-->
<mapper namespace="cn.tedu.dao.CarMapper">
    <!--1.  动态SQL标签,用来提取共性的SQL-->
    <sql id="columns">
        id,name,color,price
    </sql>
 <select id="getname" resultType="cn.tedu.pojo.Car">
        /*此为动态获取ID的方法*/
        /*$不会拼接字符串不安全,#会自动拼接字符串安全避免了sql攻击问题*/
        select   <include refid="colunm"></include>
        from car
        <where>
       <if test="name !=null">
         name = #{name}
    </if>
    </where>

    </select>

测试类的代码

 public class Test05 {
    SqlSessionFactory factory ;
    @BeforeEach
    public void  init() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
         factory = new SqlSessionFactoryBuilder().build(in);
    }


@Test
    public void getname(){

            SqlSession session=factory.openSession();
            CarDao dao = session.getMapper(CarDao.class);
            Car name3 = dao.getname("bwm");
            System.out.println(name3);
        }
}

foreach 标签测试

public class Test05 {
    SqlSessionFactory factory ;
    @BeforeEach
    public void  init() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
         factory = new SqlSessionFactoryBuilder().build(in);
    }

<!--查询id为1或者2的car的数据-->
    <select id="getByIds" resultType="cn.tedu.pojo.Car">
        select * from car where id
        in
        /*获取数组里的数据,collection的值是固定值array
        open是以啥开始  close是以啥结束  separator是以啥分隔 item是表示数组里的每个数据
        */
       /*获取循环 collection的值如果数组为array,集合为list,map的型写key
        separator=","将","截取出来, item="i"将数据方法i上,
        */
        <foreach collection="array" open="(" close=")" separator="," item="i">
            #{i}
        </foreach>
    </select>

修改接口文件,添加新方法

List<Car> getByIds(Integer[] ids);/*查询id为1或者2的car的数据*/

 在测试类的代码

 @Test
    public void get(){
        SqlSession session = factory.openSession();
        CarMapper mapper = session.getMapper(CarMapper.class);
        Car c = mapper.getById(2);//按照id查
        System.out.println(c);
 Integer[] ids = {1, 2};
        //   mapper.getByIds(new Integer[]{1, 2});
        //给SQL传入多个参数,SQL语句里通过foreach标签解析参数
        List<Car> list = mapper.getByIds(ids);
        for (Car cc : list) {
            System.out.println(cc);
        }

    }

resultmap
–1,作用:
通常,如果类里的属性名  和 表里的 字段名 一致时,可以自动的ORM
1
如果,表里字段的名字 和 类里属性的名字不一样时,resultType无法完成ORM,需要使用resultMap

–2,用法:
把SQL标签的属性从resultType改成resultMap,另外单独处理那些 表里字段的名字  和 类里属性的名字不一样 的特殊情况
 

首先创建一个封装类

package cn.tedu.pojo;

public class UserExtra {
    private Integer id;
    private Integer userId; //?
    private String work;
    private Double salary;

    @Override
    public String toString() {
        return "UserExtra{" +
                "id=" + id +
                ", userId=" + userId +
                ", work='" + work + '\'' +
                ", salary=" + salary +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getWork() {
        return work;
    }

    public void setWork(String work) {
        this.work = work;
    }

    public Double getSalary() {
        return salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }
}

创建一个接口

package cn.tedu.dao;
import cn.tedu.pojo.UserExtra;
import java.util.List;

public interface UserExtraMapper {
    List<UserExtra> selectList();//查所有数据
}

创建映射文件

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace的值是接口的全路径-->
<mapper namespace="cn.tedu.dao.UserExtraMapper">
    
    <!--id的值,是接口里的方法名  resultType的值,是要把结果封装给哪个 类的全路径-->
    <select id="selectList" resultType="cn.tedu.pojo.UserExtra">
        select * from user_extra
    </select>
    
</mapper

配置核心文件并引入映射文件

  <!--引入mapper文件-->
    <mappers>
        <mapper resource="CarMapper.xml"></mapper>
        <!--引入新的mapper文件-->
        <mapper resource="UserExtraMapper.xml"></mapper>
    </mappers
99942

测试类

@Test
    public void get2(){
        //1,开启会话
        SqlSession session = factory.openSession();
        //2,获取接口
        UserExtraMapper mapper = session.getMapper(UserExtraMapper.class);
        //3,调用方法
        List<UserExtra> list = mapper.selectList();

        for (UserExtra ue : list) {
            System.out.println(ue);
        }
    }

打印结果,此时 的第二列的值打印的是null

UserExtra{id=1, userId=null, work='程序员', salary=100000.0}
UserExtra{id=2, userId=null, work='教师', salary=1000.0}
UserExtra{id=3, userId=null, work='CTO', salary=100000.0}

解决方案

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--namespace的值是接口的全路径-->
<mapper namespace="cn.tedu.dao.UserExtraMapper">

    <!--当字段名和属性名不一致时用resultMap
        id属性是,每个resultMap的唯一标识
        type属性是,要把结果ORM给哪个类的 全路径
    -->
    <resultMap id="UserERM" type="cn.tedu.pojo.UserExtra">
        <!--描述特殊的字段,column描述表里的字段名  property描述类里的属性名  -->
        <result column="user_id" property="userId"></result>
    </resultMap>

    <!--id的值,是接口里的方法名  resultType的值,是要把结果封装给哪个 类的全路径-->
    <!--<select id="selectList" resultType="cn.tedu.pojo.UserExtra"> 特殊字段没有ORM-->
    <select id="selectList" resultMap="UserERM">
        select * from user_extra
    </select>

</mapper>

此时的打印结果为

UserExtra{id=1, userId=1, work='程序员', salary=100000.0}
UserExtra{id=2, userId=2, work='教师', salary=1000.0}
UserExtra{id=3, userId=3, work='CTO', salary=100000.0}

普通的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">
            <transactionManager type="JDBC"></transactionManager>
            <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>

创建映射文件

<?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="userMapperNS">
    <!--查询user表里的所有数据
        id属性是每条SQL的唯一标识,值是任意的
        resultType属性用来完成ORM,把表里每个字段的值 自动映射给 类里的属性
    -->
    <select id="getAll" resultType="cn.tedu.pojo.User">
         select * from user
    </select>
    
	<!--查询id=1的用户信息 resultType需要指定一个类的全路径-->
    <select id="getById" resultType="cn.tedu.pojo.User">
        select * from user where id=1
    </select>


</mapper>

创建封装类

package cn.tedu.pojo;
//为了 封装User表里的每个字段的值
//要求:类名和表名对应,属性名和字段名一致
public class User {
    private Integer id ;
    private String name ;
    private String addr ;
    private Integer age ;

    //set get toString
    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 +
                '}';
    }
}

创建测试类

package cn.tedu.mybatis;
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 Test1 {
    @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("userMapperNS.getAll");
        //5,遍历list并打印
        for (User u : list) {
            System.out.println(u);
        }

		//Mybatis为了提高查询效率,会提供缓存的技术--减少了和数据库的交互次数
        //当第一次查询数据时,缓存中当然没有,直接发起SQL查询了数据库,把数据存入缓存
        //第二次查询 相同的数据时,先去查缓存,有就直接用,没有再去查数据库
        //这是Mybatis默认就已经开启的一级缓存/SqlSession级别,必须是同一个SqlSession。
        List<User> list2 = session.selectList("userMapperNS.getAll");
        //5,遍历list并打印
        for (User u : list2) {
            System.out.println(u);
        }

        //根据id查询,selectOne()只返回一个结果
        User u = session.selectOne("userMapperNS.getById");
        System.out.println(u);
        
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值