Mybatis

  1. Mybatis介绍

  • MyBatis本是Apache软件基金会的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了Google Code,并且改名为MyBatis 。2013年11月迁移到Github。

  • MyBatis可以使用简单的XML或Annotation来配置执行SQL,并自动完成ORM操作,将执行结果返回。

  1. Mybatis框架介绍

2.1 什么是框架?

软件的半成品,解决了软件开发过程当中的普适性问题,从而简化了开发步骤,提供了开发的效率。

2.2什么是ORM框架?
2.3 使用JDBC完成ORM操作的缺点?
  • 存在大量的冗余代码。

  • 手工创建 Connection、Statement 等。

  • 手工将结果集封装成实体对象。

  • 查询效率低,没有对数据访问进行过优化(Not Cache)。

2.4访问与下载
官方网站: http://www.mybatis.org/mybatis-3/
下载地址: https://github.com/mybatis/mybatis-3/releases/tag/mybatis-3.5.1
  1. Mybatis入门—CRUD

  • 查询全部

  • 模糊查询三种写法

  • 根据id查询一条数剧

  • 添加,返回主键id

  • 批量删除

  • 修改

  • 分页

3.1建表
CREATE TABLE t_user(
    id INT NOT NULL AUTO_INCREMENT  COMMENT 'id 编号' ,
    username VARCHAR(128) NOT NULL   COMMENT 'name 姓名' ,
    password VARCHAR(128) NOT NULL   COMMENT 'password 密码' ,
    PRIMARY KEY (id)
) COMMENT = '用户表';

INSERT INTO t_user(USERNAME,PASSWORD) VALUES("jack","123");
INSERT INTO t_user(USERNAME,PASSWORD) VALUES("tom","456");
INSERT INTO t_user(USERNAME,PASSWORD) VALUES("rose","789");
3.2pom.xml中引入MyBatis核心依赖

在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>创项目自己设置</groupId>
    <artifactId>mybatis-01</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- 导入mybatis依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>

        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

        <!-- log4j日志 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

    </dependencies>

</project>
3.3 创建log4j.properties配置文件

在resources目录下创建log4j.properties

log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

级别

描述

ALL LEVEL

打开所有日志记录开关;是最低等级的,用于打开所有日志记录。

DEBUG

输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。

INFO

输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。

WARN

输出警告信息;表明会出现潜在错误的情形。

ERROR

输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。

FATAL

输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。

OFF LEVEL

关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。

3.4创建mybatis-config.xml配置文件

在resources目录下创建并配置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>
    <!-- 配置外部文件 -->
    <properties resource="db.properties"></properties>
    
    <!-- 配置日志(log4j官方配置) -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
    <!-- 根据需要给实体类配置别名 -->
    <typeAliases>
        <typeAlias type="com.pojo.User" alias="user"></typeAlias>
    </typeAliases>
    
    <!-- 官网环境配置 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <!-- 配置数据源(连接池) -->
            <dataSource type="POOLED">
                <property name="driver" value="${db.driverClassName}"/>
                <property name="url" value="${db.url}"/>
                <property name="username" value="${db.username}"/>
                <property name="password" value="${db.password}"/>
            </dataSource>
        </environment>
    </environments>

</configuration>
3.5定义实体类

定义所需CURD操作的实体类

package com.pojo;

import lombok.Data;

@Data
public class User{

    private Integer id;
    private String username;
    private String password;

}
3.6定义接口

在接口中创建相关方法,接口命名不同公司会有不同要求,一般是:xxDao 或者 xxMapper

package com.dao;
import com.pojo.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface UserDao {
    /**
     * @parea selectUser查询user表下所有的数据
     * @parea selectLike1模糊查询
     * @parea selectLike2模糊查询
     * @parea selectLike3模糊查询
     * @parea selsecOne查询一条数据
     * @return
     */
    List<User> selectUser();
    List<User> selectLike1(String username);
    List<User> selectLike2(String username);
    List<User> selectLike3(String username);
    User selsecOne(int id);

    /**
     * 添加  查询所添加数据的id
     * @param user
     * @return
     */
    int adds(User user);

    /**
     * 批量删除
     * @param id
     * @return
     */
    int del(int[] id);

    /**
     * 修改
     * @param user
     * @return
     */
    void update(User user);

    /**
     *打印数据库数据数量
     * @return
     */
    Integer getCount();

    /**
     *如果方法中有多个参数的话,必须在参数的前面使用@Param注解 名称 和sql语句中#{}  名称保持一致
     * @param first
     * @param lsat
     * @return
     */
    List<User> pagenum(@Param("first") Integer first,@Param("last") Integer lsat);
}
3.7编写Mapper.xml

在resources目录中下创建与接口对应的路径和对应名称的Mapper.xml文件

注意:如果是在resources目录下创建多个目录,创建时为:com/xxx/yyy(显示的时候为:com.xxx.yyy),切记!!!

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

<!--写SQL语句-->
<!--namespace = 所需实现的接口全限定名-->
<mapper namespace="com.dao.UserDao">
    <!-- 属性:id 对应接口下面的方法名
    resultType-->
    <!--    起别名-->
        <resultMap id="userMap" type="User">
<!--        <resultMap id="userMap" type="com.pojo.User">-->
            <!-- 主键属性的声明 -->
            <id property="id" column="id"></id>
            <!-- 其他属性的声明 -->
            <result property="username" column="username"></result>
            <result property="password" column="password"></result>
        </resultMap>

<!--    查询全部-->
    <select id="selectUser" resultType="User">
<!--    <select id="selectUser" resultType="com.pojo.User">-->
        select id, username, password
        from user
    </select>

<!--    模糊查询写法1-->
    <select id="selectLike1" resultType="com.pojo.User">
        select *
        from user
        where username like #{username}
    </select>

<!--    模糊查询写法2-->
    <select id="selectLike2" resultType="com.pojo.User">
        select *
        from user
        where username like concat('%', #{name}, '%')
    </select>

    <!-- 模糊查询3
        #{} 和 ${}区别:
        1.#{}是占位符,可以防止sql注入;${}是拼接符,不可以防止sql注入
        2.能用#{}的地方尽量使用#{},在使用order by语句时,必须使用${}
        3.只有一个基本数据类型的参数时,${}中的名称建议写value,不要写其它名称,会报错
        (Mybatis3.4版本中存在这种现象,3.5版本修改后就没有了)
    -->
    <select id="selectLike3" resultType="com.pojo.User">
        select *
        from user
        where username like "%${value}%"
    </select>

<!--查询一条数据-->
    <select id="selsecOne" resultType="com.pojo.User">
        select *
        from user
        where id = #{id}
    </select>

<!--    添加一条数据   返回主键id-->
    <insert id="adds">
        <!--select last_insert_id()获取最后添加字段的id
         keyProperty 对象的属性名
         keyColumn字段的属性名
         resultType返回的类型
        order  排序的   执行sql之前还是之后返回  AFTER  之后-->
        <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
            select last_insert_id()
        </selectKey>
        insert into user(username, password) values(#{username}, #{password})
    </insert>

<!--    批量删除-->
    <delete id="del">
        delete from user
        <where>
            id in
            <!--foreach 动态sql的标签 循环 遍历数组(array)集合(list) 可以批量处理 批量新增 批量删除
                常用属性:
                collection 对应的参数的类型 小写,固定 内置
                item 元素变量
                separator 多个值直接间隔符
                open 开始的符号
                close 关闭的符合
                内部还是根据 #{元素变量} 拼接内容-->
            <foreach collection="array" item="i" separator="," open="(" close=")">
                #{i}
            </foreach>
        </where>
    </delete>

<!--    修改-->
    <update id="update">
        update user
        set username=#{username},
            password=#{password}
        where id = #{id}
    </update>

<!--    获取数据库数据数量-->
    <select id="getCount" resultType="int">
        select count(*)
        from user
    </select>

<!--    分页-->
    <select id="pagenum" resultType="User">
        select *
        from user limit #{first},#{last}
    </select>
</mapper>
3.8注册Mapper
 <!--Mapper注册-->
    <mappers>
        <!--注册Mapper文件的所在位置-->
        <!-- 指定映射配置文件的位置,映射配置文件指的是每个接口独立的配置文件 -->
        <!--三种写法  选一即可-->
        <!-- 加载普通的xml文件,传入xml的相对路径(相对于类路径)-->
        <mapper resource="com/dao/UserMapper.xml"></mapper>
        <!-- 使用mapper接口的全限定名来加载-->
        <mapper class="com.dao.UserDao"/>
        <!-- 扫描指定包下的所有mapper-->
        <package name="com.dao"/>
    </mappers>

注意:用后两种方式加载mapper接口和mapper.xml映射文件时,可能会报错

3.9测试

在\src\test\java目录下创建测试类进行测试

//这里简单用了一个工具类  其他功能可以自行添加
public class BatisUtil {
   private static SqlSession session;

   static  {
        try {
            //读取配置文件
            InputStream res = Resources.getResourceAsStream("mybatis-config.xml");
            //创建SqlSessionFactoryBuilder 对象
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory factory = builder.build(res);
            //用SqlSessionFactory创建sqlSession对象
            //可以执行sql语句
            session = factory.openSession(true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取指定的dao对象*/
    public static <T> T getMapper(Class<T> clz){
        return session.getMapper(clz);
    }
    /**
     * 提交事务*/
    public static void commit(){
        session.commit();
    }
}

测试

  /**
     * 查询全部
     */
    @Test
    public void selectUser(){
        UserDao userDao = BatisUtil.getMapper(UserDao.class);
        System.out.println(userDao.selectUser());
        List<User> users = userDao.selectUser();
        for (User user : users) {
            System.out.println(user);
        }
    }

  /**
     * 模糊查询
     */
    @Test
    public void selectLike(){
        UserDao mapper = BatisUtil.getMapper(UserDao.class);
        //List<User> users = mapper.selectLike1("迪迦");
        //List<User> users = mapper.selectLike2("迪迦");
        List<User> users = mapper.selectLike3("迪迦");
        for (User user : users) {
            System.out.println(user);
        }
    }


  /**
     * 查询一条数据
     */
    @Test
    public void selsecOne(){
        System.out.println( BatisUtil.getMapper(UserDao.class).selsecOne(1));
    }

    /**
     * 添加  主键返回
     */
    @Test
    public void adds(){
        UserDao mapper = BatisUtil.getMapper(UserDao.class);
        User user = new User();
        user.setUsername("戴拿");
        user.setPassword(666);
        mapper.adds(user);
        System.out.println("添加字段的id为"+ user.getId());
    }

    /**
     * 批量删除
     */
    @Test
    public void del(){
        UserDao userDao = BatisUtil.getMapper(UserDao.class);
        //根据数据库字段自行添加
        int[] objs={11,12,13};
        System.out.println(userDao.del(objs));
    }

    /**
     * 修改
     */
    @Test
    public void update(){
        UserDao mapper = BatisUtil.getMapper(UserDao.class);
        User user = new User();
        user.setUsername("闪耀迪迦");
        user.setPassword(999);
        user.setId(21);
        mapper.update(user);
    }

    /**
     * 查询全部数据
     */
    @Test
    public void getCount(){
        UserDao mapper = BatisUtil.getMapper(UserDao.class);
        Integer count = mapper.getCount();
        System.out.println(count);
    }

分页测试

   @Test
    public void t1(){
        UserDao mapper = BatisUtil.getMapper(UserDao.class);
        /**
         * @Param 第一个参数: 表示当前页  pageNum
         * @Param 第二个参数:  每页显示的条数
         */
        PageHelper.startPage(1,3);
        List<User> users = mapper.selectUser();

        //users所有的数据,将所有的数据封装到 分页插件中 让其所有数据实现分页
        PageInfo<User> userPageInfo = new PageInfo<>(users);
        //System.out.println(userPageInfo);
        //System.out.println("分页的数据:" + userPageInfo.getList());


        //打印该页所有数据
        List<User> list = userPageInfo.getList();
        for (User user : list) {
            System.out.println(user);
        }
        /**
         * @Param getPageNum  当前页
         * @Param getPageSize  每页显示的条数
         * @Param getTotal   总条数
         * @Param getPages    总页数
         */
        System.out.println("当前页:" + userPageInfo.getPageNum());
        System.out.println("每页显示的条数:" + userPageInfo.getPageSize());
        System.out.println("总条数:" + userPageInfo.getTotal());//long
        System.out.println("总页数:" + userPageInfo.getPages());
    }
  1. MyBatis处理关联关系-多表连接

实体间的关系:关联关系

  • OneToOne:一对一关系(Passenger --- Passport):一个旅客只有一个护照,一个护照只对应一个旅客

  • OneToMany:一对多关系(Department --- Employee):一个部门有多个员工,一个员工只属于一个部门

  • ManyToMany:多对多关系(Student --- Subject):一个学生学习多门课程,一个课程被多个学生学习

4.1一对一(OneToOne

建表

-- 旅客表
CREATE TABLE t_passenger(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    sex VARCHAR(1),
    birthday DATE
)DEFAULT CHARSET =utf8;
 
INSERT INTO t_passenger VALUES(1,'zhansan','f','2020-11-11');
INSERT INTO t_passenger VALUES(2,'lucy','m','2020-12-12');

-- 护照表
CREATE TABLE t_passport(
    id INT PRIMARY KEY AUTO_INCREMENT,
    nationality VARCHAR(50),
    expire DATE,
    passenger_id INT UNIQUE
)DEFAULT CHARSET =utf8;


INSERT INTO t_passport VALUES(10001,'China','2030-11-11',1);
INSERT INTO t_passport VALUES(10002,'America','2030-12-12',2);

创建对应的实体类

package com.pojo;
import lombok.Data;
import java.util.Date;

/**
 * 旅客
 */
@Data
public class Passenger {

    private Integer id;
    private String name;
    private String sex;
    private Date birthDay;

    //用于映射查询对应的相关信息,一的一方
    private Passport passport;
}



package com.pojo;
import lombok.Data;
import java.util.Date;

/**
 * 护照
 */
@Data
public class Passport {

    private Integer id;
    private String nationality;
    private Date expire;
    private Integer passengerId;

}

创建接口

package com.dao;
import com.pojo.Passenger;

public interface PassengerDao {
    /**
     * 根据id查询旅客
     * @param id
     * @return
     */
    public Passenger findByPassengerId(Integer id);
}

创建PassengerMapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dao.PassengerDao">
    <!-- 声明resultMap -->
    <resultMap id="userMap" type="com.pojo.Passenger">
        <!-- 主键属性的声明 -->
        <id property="id" column="id"></id>
        <!-- 其他属性的声明 -->
        <result property="name" column="name"></result>
        <result property="sex" column="sex"></result>
        <result property="birthDay" column="birthDay"></result>

        <!-- 一的一方映射 -->
        <association property="passport" javaType="com.pojo.Passport">
            <!-- 主键属性的声明 -->
            <id property="id" column="passportId"></id>
            <!-- 其他属性的声明 -->
            <result property="nationality" column="nationality"></result>
            <result property="expire" column="expire"></result>
        </association>

    </resultMap>

    <!-- 查询所有用户,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
    <select id="findByPassengerId" resultMap="userMap">
        SELECT p1.id,p1.name,p1.sex,p1.birthday,p2.id AS passportId,p2.nationality,p2.expire
        FROM t_passenger p1
        INNER JOIN t_passport p2
        ON p1.id = p2.passenger_id
        WHERE p1.id = #{id}
    </select>

</mapper>

测试

  @Test
    public void findByPassengerId(){
        PassengerDao mapper = BatisUtil.getMapper(PassengerDao.class);
        Passenger byPassengerId = mapper.findByPassengerId(1);
        System.out.println(byPassengerId);
    }
4.2一对多(OneToMany

创建表

CREATE TABLE t_department(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50),
    location VARCHAR(100)
)DEFAULT CHARSET =utf8;

INSERT INTO t_department VALUES(1,"教学部","北京"),(2,"研发部","上海");

CREATE TABLE t_employee(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50),
    salary DOUBLE,
    dept_id INT
)DEFAULT CHARSET =utf8;

INSERT INTO t_employee VALUES(1,"jack",1000.5,1);
INSERT INTO t_employee VALUES(2,"rose",2000.5,1);
INSERT INTO t_employee VALUES(3,"张三",3000.5,2);
INSERT INTO t_employee VALUES(4,"李四",4000.5,2);

创建实体类

/**
 * 部门
 */
@Data
public class Department {

    private Integer id;
    private String name;
    private String location;
}

/**
 * 员工
 */
@Data
public class Employee {

    private Integer id;
    private String name;
    private Double salary;

    //一的一方,写的是对象属性
    private Department department;

创建接口

public interface DepartmentDao {
    /**
     * 根据id查询部门
     * @param id
     * @return
     */
    public Department selectByIdDepartment(Integer id);
   
}

创建DepartmentMapper.xml配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dao.DepartmentDao">
    <!-- 声明resultMap -->
    <resultMap id="userMap" type="com.pojo.Department">
        <!-- 主键属性的声明 -->
        <id property="id" column="id"></id>
        <!-- 其他属性的声明 -->
        <result property="name" column="name"></result>
        <result property="location" column="location"></result>

        <!-- 一的一方映射 -->
        <collection  property="employeeList" ofType="com.pojo.Employee">
            <!-- 主键属性的声明 -->
            <id property="id" column="empId"></id>
            <!-- 其他属性的声明 -->
            <result property="name" column="empName"></result>
            <result property="salary" column="salary"></result>
        </collection>

    </resultMap>

    <!-- 查询所有部门,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
    <select id="selectByIdDepartment" resultMap="userMap">
        SELECT d.id,d.name,d.location,e.id AS empId,e.name AS empName,e.salary
        FROM t_department d INNER JOIN t_employee e ON d.id = e.dept_id
        WHERE d.id = #{id}
    </select>
</mapper>

测试

   @Test
    public void selectByIdDepartment(){
        DepartmentDao mapper = BatisUtil.getMapper(DepartmentDao.class);
        Department department = mapper.selectByIdDepartment(1);
        System.out.println(department);
    }
4.3多对多(ManyToMany)

建表

CREATE TABLE t_student(

    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50),
    sex VARCHAR(1)

)DEFAULT CHARSET = utf8;

INSERT INTO t_student VALUES(1,'jack','m');
INSERT INTO t_student VALUES(2,'rose','f');


CREATE TABLE t_subject(

    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(50),
    grade INT

)DEFAULT CHARSET = utf8;

INSERT INTO t_subject VALUES(1001,'JavaSE',1);
INSERT INTO t_subject VALUES(1002,'JavaEE',2);

CREATE TABLE t_stu_sub(

    student_id INT,
    subject_id INT

)DEFAULT CHARSET = utf8;

INSERT INTO t_stu_sub VALUES(1,1001);
INSERT INTO t_stu_sub VALUES(1,1002);
INSERT INTO t_stu_sub VALUES(2,1001);
INSERT INTO t_stu_sub VALUES(2,1002);

实体类

/**
 * 学生
 */
@Data
public class Student {

    private Integer id;
    private String name;
    private String sex;

    //多对多,都是多的一方
    private List<Subject> subjectList;
}

/**
 * 课程
 */
@Data
public class Subject {

    private Integer id;
    private String name;
    private Integer grade;
}

创建接口

public interface StudentDao {
  /**
     * 通过id查询学生以及对应的课程
     * @param id
     * @return
     */
   public Student findByStudentId(Integer id);
}

创建StudentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dao.StudentDao">
    <!-- 声明resultMap -->
    <resultMap id="userMap" type="com.pojo.Student">
        <!-- 主键属性的声明 -->
        <id property="id" column="id"></id>
        <!-- 其他属性的声明 -->
        <result property="name" column="name"></result>
        <result property="sex" column="sex"></result>

        <!-- 一的一方映射 -->
            <collection  property="subjectList" ofType="com.pojo.Subject">
            <!-- 主键属性的声明 -->
            <id property="id" column="subjectId"></id>
            <!-- 其他属性的声明 -->
            <result property="name" column="subjuectName"></result>
            <result property="grade" column="grade"></result>
        </collection>

    </resultMap>

    <!-- 查询所有用户,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
    <select id="findByStudentId" resultMap="userMap">
        SELECT sy.id,sy.Name,sy.sex,su.id as subjectId,su.NAME as subjuectName,su.grade
        FROM t_student sy
        INNER JOIN t_stu_sub
        on sy.id=t_stu_sub.student_id
        INNER JOIN t_subject su
        on t_stu_sub.subject_id=su.id
        where sy.id = #{id}
    </select>

</mapper>

测试

 @Test
    public void findByStudentId(){
        StudentDao mapper = BatisUtil.getMapper(StudentDao.class);
        Student student = mapper.findByStudentId(1);
        System.out.println(student);
    }
4.4 关系总结
双方均可建立关系属性,建立关系属性后,在对应的Mapper文件中使用< ResultMap >完成多表映射。
如果对应的对象是一的一方,使用< association property="" javaType="" >
如果对应的对象是多的一方,使用< collection property="" ofType="" >
  1. 动态SQL

MyBatis的映射文件中支持在基础SQL上添加一些逻辑操作,并动态拼接成完整的SQL之后再执行,以达到SQL复用、简化编程的效果

5.1准备
创建表


CREATE TABLE `t_car`  (
  `id` INT(11) PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(256),
  `brand` VARCHAR(256),
  `price` DOUBLE,
  `color` VARCHAR(256),
  `num` INT(11)
) ENGINE = INNODB  CHARACTER SET = utf8;

INSERT INTO t_car(NAME,brand,price,color,num) VALUES('迈腾','大众',200000,'黑色',5000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('帕萨特','大众',200000,'黑色',6000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('卡宴','保时捷',700000,'白色',2000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('奥迪Q3','奥迪',300000,'白色',3000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('宝马X5','宝马',500000,'黑色',2000);
INSERT INTO t_car(NAME,brand,price,color,num) VALUES('雅阁','本田',180000,'黑色',3000);
实体类

/**
 * 如果对象涉及到IO流传以及缓存操作,必须实现序列化接口
 * public class Car implements Serializable
 */

@Data
public class Car{

    private Integer id;
    private String name;
    private String brand;
    private Double price;
    private String color;
    private Integer num;

}
//创建CarDao

public interface CarDao {
    /**
     * 查询全部
     * @return
     */
    public List<Car> findAll();

    /**
     * 根据id查询一个
     * @param id
     * @return
     */
    public Car findAllById(Integer id);

    /**
     * 模糊查询
     * @param carVo
     * @return
     */
    public List<Car> findCar(CarVo carVo);

    /**
     * 修改
     * @param car
     */
    public void update(Car car);

    /**
     * 批量删除
     * @param id
     * @return
     */
    public int delect(int[] id);
}
<!--创建CarMapper.xml,编写相关代码测试动态SQL标签-->

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dao.CarDao">
    <!-- 声明resultMap -->
    <resultMap id="CarMap" type="com.pojo.Car">
        <!-- 主键属性的声明 -->
        <id property="id" column="id"></id>
        <!-- 其他属性的声明 -->
        <result property="name" column="name"></result>
        <result property="brand" column="brand"></result>
        <result property="price" column="price"></result>
        <result property="color" column="color"></result>
        <result property="num" column="num"></result>
    </resultMap>
</mapper>
5.2<sql>

在CarMapper.xml中添加对应查询所有和查询单个的方法

<sql id="baseSql">
        select id,name,brand,price,color,num from t_car
</sql>

<!--    查询所有用户,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
    <select id="findAll" resultMap="CarMap">
        <!-- 引入sql片段 -->
        <include refid="baseSql"></include>
    </select>

<!--    根据id查询一个用户-->
    <select id="findAllById" resultMap="CarMap">
        <!-- 引入sql片段 -->
        <include refid="baseSql"></include>
        where id = #{id}
    </select>
5.3< if > 和 < where >

使用条件查询进行测试

 <select id="findCar" resultMap="CarMap">
    <include refid="baseSql"></include>
     <where>
            <if test="CarName != null and CarName !=''">
                name=#{CarName}
            </if>
            <if test="CarBrand != null and CarBrand !=''">
                and brand=#{CarBrand}
            </if>
            <if test="CarColor != null and CarColor !=''">
                and color=#{CarColor}
            </if>
            <if test="CarPrice != null and CarPrice !=''">
                and price=#{CarPrice}
            </if>
     </where>
</select>
5.4< set >和< trim >

set:可以忽略判断中间隔的都逗号

trim:< trim prefix="" suffix="" prefixOverrides="" suffixOverrides="" >代替< where > 、< set >同样是修改,写法不同而已

    <update id="update">
        update t_car
<!--        <set>-->
 <!-- set标签可以忽略逗号 -->
<!--            <if test="name !=null and name!=''">-->
<!--                name=#{name},-->
<!--            </if>-->
<!--            <if test="color !=null and color!=''">-->
<!--                color=#{color}-->
<!--            </if>-->
<!--            <where>-->
<!--                <if test="id!=null">-->
<!--                    id = #{id}-->
<!--                </if>-->
<!--            </where>-->
<!--        </set>-->
        <trim prefix="set" prefixOverrides=",">
            <if test="name !=null and name !='' ">
                name = #{name},
            </if>
            <if test="color !=null and color !='' ">
                color = #{color}
            </if>
        </trim>
        <trim prefix="where" prefixOverrides="and|or">
            <if test="id != null">
                id = #{id}
            </if>
        </trim>
    </update>
5.5< foreach >
    <!--foreach 动态sql的标签 循环 遍历数组(array)集合(list) 可以批量处理 批量新增 批量删除
                常用属性:
                collection 对应的参数的类型 小写,固定 内置
                           如果参数是数组,则值为:array
                           如果参数是List集合,则值为:list
                           如果参数是set或者其他集合,则值为:@Param("值")
                item 元素变量
                separator 多个值直接间隔符
                open 开始的符号
                close 关闭的符合
                内部还是根据 #{元素变量} 拼接内容-->
    <delete id="delect">
       delete from t_car
        <where>
            id in
            <foreach collection="array" item="id" separator="," open="(" close=")">
            #{id}
            </foreach>
        </where>
    </delete>
  1. 注解写法(了解)

MyBatis除了使用xml方式操作数据库,也可以通过在接口中直接添加MyBatis注解,进行操作

将SQL语句写到代码文件中,后续的维护性和扩展性不是很好(如果想修改SQL语句,就得改代码,得重新打包部署,而如果用xml方式,则只需要修改xml,用新的xml取替换旧的xml)

另外mapper.xml就可以省掉了 可以直接在dao层写sql语句,然后在全局配置文件中直接指定加载这个类

@Repository//一般用在持久层
public interface UserDao {
    /**
     * 查询所有用户
     * @return
     */
  @Select("select id,username,password from user")
    List<User> selectAllUser();
}
  1. 缓存

内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。

7.1一级缓存

一级缓存是SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。

注意:无需任何配置,默认开启一级缓存

在CarDao中添加相关方法

//查询单个
public Car findAllById(Integer id);

在CarMapper.xml中添加相关代码

<!-- 查询单个 -->
<select id="findAllById" resultMap="carMap">
    <!-- 引入sql片段 -->
    <include refid="baseSql"></include>
    <where>
        id = #{id}
    </where>
</select>

测试类中进行测试

 @Test
    public void findAllById() throws IOException {
           InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //使用同一个sqlSession进行操作
        SqlSession sqlSession = sessionFactory.openSession();
        CarDao mapper = sqlSession.getMapper(CarDao.class);
        Car one = mapper.selectAllByOne(1);
        //手动清楚一级缓存
        //sqlSession.clearCache();
        Car ome1 = mapper.selectAllByOne(1);

        //验证一级缓存是否存在
        System.out.println(one == ome1);
        //关闭
        sqlSession.close();
        inputStream.close();
    }
7.2二级缓存

SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的不同SqlSession发起的多次同构查询,会将数据保存在二级缓存中

注:在sqlSession.commit()或者sqlSession.close()之后生效。

开启全局缓存

< settings >是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。

<configuration>
    <properties .../>
      
      <!-- 注意书写位置 -->
    <settings>
        <!--true为开启 -->
        <setting name="cacheEnabled" value="false"/> <!-- mybaits-config.xml中开启全局缓存(默认开启) -->
    </settings>
      <typeAliases></typeAliases>
</configuration>

指定Mapper缓存

在CarMapper.xml中添加相关代码

<!-- 开启当前二级缓存 -->
<cache/>

测试

   /**
     * 二级缓存
     */
    @Test
    public void findAllById2() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        //使用同一个sqlSession进行操作
        SqlSession sqlSession1 = sessionFactory.openSession();
        CarDao mapper1 = sqlSession1.getMapper(CarDao.class);
        Car one1 = mapper1.selectAllByOne(1);
        sqlSession1.close();
        SqlSession sqlSession2 = sessionFactory.openSession();
        CarDao mapper2 = sqlSession2.getMapper(CarDao.class);
        Car one2 = mapper2.selectAllByOne(1);
    }

8.嵌套查询和延迟加载

相关准备:这里用之前的部门表

相关的实体类上不能使用@Data@ToString等注解,否则会导致延迟加载失效

//实体类


/**
 * 员工
 * 实现延迟加载不能使用@Data,@ToString 注解
 */

//@Data
public class Employee {

    private Integer id;
    private String name;
    private Double salary;

    //一的一方,写的是对象属性
    private Department department;
    
    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 Double getSalary() {
        return salary;
    }

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

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                ", department=" + department +
                '}';}
}

编写EmployeeDao

public interface EmployeeDao {
    
    /**
     * 通过id查询员工
     * @return
     */
    public Employee selectAllById(Integer id);
}

配置EmployeeMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dao.EmployeeDao">
    <!-- 声明resultMap -->
    <resultMap id="userMap" type="com.pojo.Employee">
        <!-- 主键属性的声明 -->
        <id property="id" column="empId"></id>
        <!-- 其他属性的声明 -->
        <result property="name" column="empName"></result>
        <result property="salary" column="salary"></result>
        

        <!-- 嵌套查询
        fetchType="lazy" 表示懒加载,延迟加载
        fetchType="eager" 表示立即加载
        -->
<association property="department" javaType="com.pojo.Department"
             select="com.dao.DepartmentDao.selectAllById" column="dept_id"
fetchType="lazy">
</association>
    </resultMap>

    <!-- 只查询单个员工 -->
    <select id="selectAllById" resultMap="userMap">
        SELECT e.id AS empId,e.name AS empName,e.salary,e.dept_id
        FROM t_employee e
        WHERE e.id = #{id}
    </select>
</mapper>

配置DepartmentMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.dao.DepartmentDao">
    <!-- 声明resultMap -->
    <resultMap id="userMap" type="com.pojo.Department">
        <!-- 主键属性的声明 -->
        <id property="id" column="id"></id>
        <!-- 其他属性的声明 -->
        <result property="name" column="name"></result>
        <result property="location" column="location"></result>

        <!-- 一的一方映射 -->
        <collection  property="employeeList" ofType="com.pojo.Employee">
            <!-- 主键属性的声明 -->
            <id property="id" column="empId"></id>
            <!-- 其他属性的声明 -->
            <result property="name" column="empName"></result>
            <result property="salary" column="salary"></result>
        </collection>

    </resultMap>

    <!-- 查询所有部门,如果属性名和数据库中的列名不一致,我们可以使用resultMap标签 -->
    <select id="selectByIdDepartment" resultMap="userMap">
        SELECT d.id,d.name,d.location,e.id AS empId,e.name AS empName,e.salary
        FROM t_department d INNER JOIN t_employee e ON d.id = e.dept_id
        WHERE d.id = #{id}
    </select>
    <!-- 只查询单个部门 -->
    <select id="selectAllById" resultMap="userMap">
        SELECT d.id,d.name,d.location
        FROM t_department d
        WHERE d.id = #{id}
    </select>
</mapper>

配置延迟加载

好处:开启延迟加载后,如果不使用及联数据,则不会触发及联查询操作,有利于加快查询速度、节省内存资源。

mybatis-config.xml中开启延迟加载配置

 <settings>
        <!-- 开启延迟加载,懒加载,默认false -->
        <setting name="lazyLoadingEnabled" value="true"/>
     
        <!-- 将积极加载改为消息加载即按需加载 -->
        <setting name="aggressiveLazyLoading" value="false" />
     
        <!-- 由于重写了实体类中toString方法,需要排除不相干的方法导致的立即加载情况-->
        <setting name="lazyLoadTriggerMethods" value="toString()"/>
</settings>

测试

/**
  * 根据部门id查询员工
  */
    @Test
    public void selectAllById(){
        EmployeeDao mapper = BatisUtil.getMapper(EmployeeDao.class);
        Employee employee = mapper.selectAllById(1);
        System.out.println(employee.getDepartment().getName());
    }
  1. Mybatis其他相关

$符号

${attribute} 属于字符串拼接SQL,而非预编译占位符,会有注入攻击问题,不建议在常规SQL中使用,常用于可解决动态生降序问题。

public List<User> selectAllUsers1(User user); // ${name} ${id} 可获取user中的属性值
public List<User> selectAllUsers2(@Param("rule") String rule); //必须使用@Param否则会作为属性解析
<select id="selectAllUsers1" resultType="user">
    SELECT * FROM t_users 
    WHERE name = '${name}' or id = ${id} <!-- 拼接name和id,如果是字符类型需要用单引号:'${name}' -->
</select>
<select id="selectAllUsers2" resultType="user">
    SELECT * FROM t_users 
      ORDER BY id ${rule} <!-- 拼接 asc | desc -->
</select>
User user = new User(....);
List<User> ulist1 = userDAO.selectAllUsers1(user); //调用时传入user对象

List<User> ulist2 = userDao.selectAllUsers2("desc"); //调用时传入asc | desc
<select id="selectUsersByKeyword" resultType="user">
    SELECT * FROM t_user
      WHERE name = '${name}' <!-- 会存在注入攻击  比如传入参数是 【String name = "tom' or '1'='1";】-->
</select>
Lombok

简化类,通过注解快速实现类的getter和setter和构造和toString等方法

使用步骤:

  1. Idea安装Lombok插件

  1. file-->Settings-->Plugins-->搜索lombok

依赖jar

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.22</version>
</dependency>

编写类

使用注解:

@Data:自动实现 getter和setter 重写toString

@NoArgsConstructor 自动实现无参构造

@AllArgsConstructor 自动实现全参构造

Java标准类:Java Bean

1.属性私有

2.提供公有的getter和setter方法

3.提供无参构造函数

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值