mybatis基本知识和基本使用

文章目录

一:MyBatis:

MyBatis是一个半自动的ORM框架,你给sql,我来执行

ORM:(Object Relation Mapping)对象关系映射,将java中的一个对象与数据表中的一行记录一一对应。

ORM框架提供了实体类与数据表的映射关系,通过映射文件的配置,将文件保存到了数据表中,实现了对象的持久化。

MyBatis特点:

  • 支持自定义sql,存储过程

  • 对原有的JDBC进行了封装,几乎消除了所有JDBC代码(打开连接,执行sql等),让开发者专注sql

  • 支持XML和注解配置(@xxxx)的方式自动完成ORM操作,实现结果映射,对象的属性和数据表的对应关系就叫映射

二:MyBatis框架部署:

框架部署:就是将框架引入到我们的项目中

过程:

1:创建Maven项目

  • java工程

  • Web工程

2:在项目中添加MyBatis依赖

https://mvnrepository.com/

  • 在pom.xml中添加依赖---- mybatis 与 mysql driver
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
 
 
 
 
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

说明,在pom.xml 中添加包的说明以后,需要到Maven中刷新一下,将包安装进来

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/27CE280D780C469B976CD3D61539A5CF/12540

3:创建MyBatis配置文件,配置数据库连接

需要先定义MyBatis配置模板,其他的模板也可以在这里进行配置

选择resources----右健New----Edit File Templates

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/9EBF17FA35C64D74BE6904911AE24660/12561

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

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/D7084595D23D4F159FFB2C3919F490BC/12570

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/7F2593486C9A4B46B6E4CC12CCC8018C/12565

在mybatis-config.xml中添加数据库连接信息
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>
<!--  environments配置数据库连接信息  -->
<!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!--    default属性,用来执行使用哪个environment 标签,-->
    <environments default="mysql">
        <environment id="mysql">
<!--      transactionManager用于配置数据库管理方法      -->
            <transactionManager type="JDBC"></transactionManager>
<!--            dataSource标签用来配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

三:MyBatis框架使用

案例:学生信息的数据库操作

1:创建数据表

CREATE TABLE tb_students(
	sid int PRIMARY key auto_increment,
	stu_num CHAR(5) not null UNIQUE,
	stu_name VARCHAR(20) not null,
	stu_gender CHAR(2) not null,
	stu_age int not null
);

2:创建实体类

导入lombok

然后添加注释

package com.liguoqing.pojo;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
 
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private int stuId;
    private String stuNum;
    private String stuName;
    private String stuGender;
    private  int stuAge;
 
}

3:创建DAO接口,并创建操作方法

package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
 
public interface StudentDAO {
    public int insertStudent(Student student);
    public int deleteStudent(Student stuNum);
}

4:创建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">
<mapper namespace="">
 
</mapper>
 

在resources目录下,新建mappers文件夹

在mappers中新建“StudentMapper”.xml的文件,这个可以根据上面的模板进行创建,注意这里的StudentMapper名称建议跟DAO联系起来

在映射文件中,对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">
<!--mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.liguoqing.dao.StudentDAO">
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
    <insert id="insertStudent" parameterType="com.liguoqing.pojo.Student">
         insert into tb_student(stu_num,stu_name,stu_gender,stu_age)
         values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
    </insert>
    <delete id="deleteStudent">
        delete  from  tb_student where stu_num = #{stuNum}
    </delete>
</mapper>

5:将映射文件添加到主配置文件

<?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>
<!--  environments配置数据库连接信息  -->
<!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!--    default属性,用来执行使用哪个environment 标签,-->
    <environments default="mysql">
        <environment id="mysql">
<!--      transactionManager用于配置数据库管理方法      -->
            <transactionManager type="JDBC"></transactionManager>
<!--            dataSource标签用来配置数据库连接信息-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql"/>
                <property name="url" value="jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
 
    <mappers>
        <mapper resource="mappers/StudentMapper.xml"></mapper>
    </mappers>
 
</configuration>

四:单元测试

ps:(测试类往往是在测试类名后加Test,测试方法往往是在测试方法名前加Test)

1: 添加单元测试依赖 junit

2:创建测试类

在被测试接口类名后面 alt + insert ——> test

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/CB741A56EDAE47CABF2F60B28CA98A66/12645

package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
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.Test;
 
import javax.annotation.Resource;
 
import java.io.IOException;
import java.io.InputStream;
 
import static org.junit.Assert.*;
 
public class StudentDAOTest {
 
    @Test
    public void insertStudent() {
 
        try {
            //加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //创建builder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //会话工厂,连接工厂
            SqlSessionFactory factory = builder.build(is);
            //sqlsession 代表数据库的连接,也代表数据库的连接对象
            //会话(连接)
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//            System.out.println(studentDAO);
            int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24));
            //需要手动提交
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
 
    }
 
    @org.junit.Test
    public void deleteStudent() {
    }
}

五:MyBatis 的CRUD操作

案例:学生信息的增删查改

1:添加操作

2:删除操作

根据学号删除一条学生信息

步骤:

  • 在StudentDAO中定义删除方法
package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
 
public interface StudentDAO {
    public int insertStudent(Student student);
    public int deleteStudent(String stuNum);
}
  • 在StudentMapper.xml中对接口方法进行"实现"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--mapper文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.liguoqing.dao.StudentDAO">
<!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
    <insert id="insertStudent" parameterType="com.liguoqing.pojo.Student">
         insert into tb_students(stu_num,stu_name,stu_gender,stu_age)
         values(#{stuNum},#{stuName},#{stuGender},#{stuAge})
    </insert>
    <delete id="deleteStudent">
        delete  from  tb_students where stu_num = #{stuNum}
    </delete>
</mapper>
  • 测试:在StudentDAO的测试类中添加测试方法

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/32D323E9C83A41D7A36E75375C8B4D02/12717

package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
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.Test;
 
import javax.annotation.Resource;
 
import java.io.IOException;
import java.io.InputStream;
 
import static org.junit.Assert.*;
 
public class StudentDAOTest {
 
    @Test
    public void insertStudent() {
 
        try {
            //加载mybatis配置文件
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //创建builder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            //会话工厂,连接工厂
            SqlSessionFactory factory = builder.build(is);
            //sqlsession 代表数据库的连接,也代表数据库的连接对象
            //会话(连接)
            SqlSession sqlSession = factory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//            System.out.println(studentDAO);
            int i = studentDAO.insertStudent(new Student(0,"10002","java少年","男",24));
            //需要手动提交
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
 
    }
 
    @Test
    public void testDeleteStudent() {
        try {
 
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
            //SqlSessionFactory表示mybatis的会话工厂,工厂模式
            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
            //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            //通过SqlSession 对象调用getMapper方法获取DAO接口对象
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
            //调用被测试方法
            int i = studentDAO.deleteStudent("10001");
            //提交事务
            sqlSession.commit();
            System.out.println(i);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
}

3:修改操作

<update id="updateStudent">
       update tb_students
       set stu_name=#{stuName},
           stu_gender=#{stuGender},
           stu_age=#{stuAge}
       where stu_num = #{stuNum}
   </update>

根据学号,修改其他字段信息

@Test
public void  testUpdateStudent(){
    try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        int i = studentDAO.updateStudent(new Student(3,"10002",".net小小少年","男",25));
        sqlSession.commit();
        assertEquals(1,i);//期望,单元测试的期望返回结果
        System.out.println(i);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

4:查询操作-查询所有

package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
 
import java.util.List;
 
public interface StudentDAO {
    public int insertStudent(Student student);
    public int deleteStudent(String stuNum);
    public int updateStudent(Student student);
    public List<Student> listStudents();
 
 
}
第一种
       resultType执行查询结果,封装的对象的实体类
        resultSets指定当前操作返回的集合类型(可省略)
        resultType返回的类型
        <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">
            select
                sid as stuId,
                stu_num as stuNum,
                stu_name as stuName,
                stu_gender as stuGender,
                stu_age as stuAge
            from tb_students
        </select>
第二种(推荐)

 
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap" type="com.liguoqing.pojo.Student">
    <id column="sid" property="stuId"/>
    <result column="stu_num" property="stuNum"/>
    <result column="stu_name" property="stuName"/>
    <result column="stu_gender" property="stuGender"/>
    <result column="stu_age" property="stuAge"/>
</resultMap>
 
<!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
    select sid,
           stu_num,
           stu_name,
           stu_gender,
           stu_age
    from tb_students
</select>
@Test
public void testStudentList() {
    try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        List<Student> studentList = studentDAO.listStudents();
        assertNotNull(studentList);
        sqlSession.commit();
        for (Student student : studentList
        ) {
            System.out.println(student);
        }
 
    } catch (IOException e) {
 
    }
 
 
}

5:查询操作-查询一条记录

根据学号查询一个学生信息

  • 在StudentDAO中定义接口方法
package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
 
import java.util.List;
 
public interface StudentDAO {
    public int insertStudent(Student student);
    public int deleteStudent(String stuNum);
    public int updateStudent(Student student);
    public List<Student> listStudents();
    public Student queryStudent(String stuNum);
 
 
}
  • 在StudentDAOMappperxml 中配置StudentDAO接口的方法实现–SQL
<?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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.liguoqing.dao.StudentDAO">
    <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
    <insert id="insertStudent" parameterType="com.liguoqing.pojo.Student">
        insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
        values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
    </insert>
 
    <delete id="deleteStudent">
        delete
        from tb_students
        where stu_num = #{stuNum}
    </delete>
 
    <update id="updateStudent">
        update tb_students
        set stu_name   = #{stuName},
            stu_gender = #{stuGender},
            stu_age    = #{stuAge}
        where stu_num = #{stuNum}
 
    </update>
 
    <!--    resultType执行查询结果,封装的对象的实体类-->
    <!--    resultSets指定当前操作返回的集合类型(可省略)-->
    <!--    resultType返回的类型-->
    <!--    <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">-->
    <!--        select-->
    <!--            sid as stuId,-->
    <!--            stu_num as stuNum,-->
    <!--            stu_name as stuName,-->
    <!--            stu_gender as stuGender,-->
    <!--            stu_age as stuAge-->
    <!--        from tb_students-->
    <!--    </select>-->
 
<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
    <resultMap id="studentMap" type="com.liguoqing.pojo.Student">
        <id column="sid" property="stuId"/>
        <result column="stu_num" property="stuNum"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_gender" property="stuGender"/>
        <result column="stu_age" property="stuAge"/>
    </resultMap>
<!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
    <select id="listStudents" resultMap="studentMap">
        select sid,
               stu_num,
               stu_name,
               stu_gender,
               stu_age
        from tb_students
    </select>
 
 
    <select id="queryStudent" resultMap="studentMap">
        select sid,
               stu_num,
               stu_name,
               stu_gender,
               stu_age
        from tb_students
        where stu_num = #{stuNum}
    </select>
 
 
 
 
</mapper>

单元测试-- 查询不需要提交事务

@Test
public void testStudent() {
    try {
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        Student student = studentDAO.queryStudent("10002");
         assertNotNull(student);
        //查询不用提交事务
        System.out.println(student);
    } catch (IOException e) {
 
    }
}

6:查询操作-多参数查询

方法的参数数量和mapper.xml中sql语句关系

​ 在MyBatis进行操作:
​ *** 1:如果操作方法只有一个简单类型或者字符串类型的参数,**
​ *** 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写**

​ *** 2:如果操作方法有一个对象类型的参数,**
​ *** 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)**

​ *** 3:如果操作方法有一个Map类型的参数,**
​ *** 在Mapper配置中可以直接通过#{key}获取key的指定value值**

​ *** 4:如果操作方法有多个参数,该如何处理呢?**
​ *** 通过MyBatis自带的参数 arg0 arg1 … 来获取相应的参数**
​ *** 也可以通过在方法的参数中加入@Param(“key”) 给参数添加注解的方式**

分页查询

  • 参数 start ,pagesize

在StudentDAO中定义操作方法,如果方法有多个参数,使用@Param 注解声明参数的别名

package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
import org.apache.ibatis.annotations.Param;
import java.util.HashMap;
import java.util.List;
 
public interface StudentDAO {
    /*
    * 在MyBatis进行操作:
    * 1:如果操作方法只有一个简单类型或者字符串类型的参数,
    * 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写
    * 2:如果操作方法有一个对象类型的参数,
    * 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
    * 3:如果操作方法有一个Map类型的参数,
    * 在Mapper配置中可以直接通过#{key}获取key的指定value值
    * 4:如果操作方法有多个参数,该如何处理呢?
    * 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数
    * 也可以通过@Param("key") 给参数添加注解的方式
     */
    public int insertStudent(Student student);
    public int deleteStudent(String stuNum);
    public int updateStudent(Student student);
    public List<Student> listStudents();
    public Student queryStudent(String stuNum);
//    public List<Student> listStudentsByPage(HashMap<String,Integer> map);
//    public List<Student> listStudentsByPage(int start,int pageSize);
    public List<Student> listStudentsByPage(@Param("start") int start
                                        ,@Param("pageSize") int pageSize);
}

在StudentMapper.xml配置sql时,使用#{}获取到指定的参数

<?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文件相当于DAO接口的实现类,namespace属性要指定实现DAO接口的全限定名(全限定名:包名加类名)-->
<!-- copy reference-->
<mapper namespace="com.liguoqing.dao.StudentDAO">
    <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
    <insert id="insertStudent" parameterType="com.liguoqing.pojo.Student">
        insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
        values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
    </insert>
 
    <delete id="deleteStudent">
        delete
        from tb_students
        where stu_num = #{stuNum}
    </delete>
 
    <update id="updateStudent">
        update tb_students
        set stu_name   = #{stuName},
            stu_gender = #{stuGender},
            stu_age    = #{stuAge}
        where stu_num = #{stuNum}
 
    </update>
 
    <!--    resultType执行查询结果,封装的对象的实体类-->
    <!--    resultSets指定当前操作返回的集合类型(可省略)-->
    <!--    resultType返回的类型-->
    <!--    <select id="listStudents" resultType="com.liguoqing.pojo.Student" resultSets="java.util.List">-->
    <!--        select-->
    <!--            sid as stuId,-->
    <!--            stu_num as stuNum,-->
    <!--            stu_name as stuName,-->
    <!--            stu_gender as stuGender,-->
    <!--            stu_age as stuAge-->
    <!--        from tb_students-->
    <!--    </select>-->
 
    <!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
    <resultMap id="studentMap" type="com.liguoqing.pojo.Student">
        <id column="sid" property="stuId"/>
        <result column="stu_num" property="stuNum"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_gender" property="stuGender"/>
        <result column="stu_age" property="stuAge"/>
    </resultMap>
    <!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
    <select id="listStudents" resultMap="studentMap">
        select sid,
               stu_num,
               stu_name,
               stu_gender,
               stu_age
        from tb_students
    </select>
 
 
    <select id="queryStudent" resultMap="studentMap">
        select sid,
               stu_num,
               stu_name,
               stu_gender,
               stu_age
        from tb_students
        where stu_num = #{stuNum}
    </select>
 
<!--    <select id="listStudentsByPage" resultMap="studentMap">-->
<!--        select sid,-->
<!--               stu_num,-->
<!--               stu_name,-->
<!--               stu_gender,-->
<!--               stu_age-->
<!--        from tb_students limit #{start}, #{pageSize}-->
<!--    </select>-->
 
        <select id="listStudentsByPage" resultMap="studentMap">
            select sid,
                   stu_num,
                   stu_name,
                   stu_gender,
                   stu_age
            from tb_students
                    limit #{start}, #{pageSize}
            <!-- limit #{arg0}, #{arg1}-->
            <!-- limit #{param1}, #{param2}-->
 
        </select>
 
 
</mapper>

—注意:如果DAO操作方法,没有通过@Param指定参数别名,在SQL中也可以通过MyBatis自带的arg0 ,arg1… 或者 param1,param2…获取参数

单元测试

  @Test
    public void testlistStudentsByPage() {
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory sqlSessionFactory = builder.build(is);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
 
//            HashMap<String,Integer> map = new HashMap<String,Integer>();
//            map.put("start",0);
//            map.put("pageSize",2);
//            List<Student> studentList = studentDAO.listStudentsByPage(map);
 
            List<Student> studentList = studentDAO.listStudentsByPage(0,2);
 
            assertNotNull(studentList);
            sqlSession.commit();
            for (Student student : studentList
            ) {
                System.out.println(student);
            }
        } catch (IOException e) {
 
        }
    }

7:查询操作-查询总记录数

StudentDAO

package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
import org.apache.ibatis.annotations.Param;
 
import java.util.HashMap;
import java.util.List;
 
public interface StudentDAO {
    /*
    * 在MyBatis进行操作:
    * 1:如果操作方法只有一个简单类型或者字符串类型的参数,
    * 在Mapper配置中可以直接通过#{key}获取,这个key的占位符号可以随便写
    * 2:如果操作方法有一个对象类型的参数,
    * 在Mapper配置中可以直接通过#{attrName}获取对象的指定属性值(attrName必须是参数对象的属性)
    * 3:如果操作方法有一个Map类型的参数,
    * 在Mapper配置中可以直接通过#{key}获取key的指定value值
    * 4:如果操作方法有多个参数,该如何处理呢?
    * 通过MyBatis自带的参数 arg0 arg1 .. 来获取相应的参数
    * 也可以通过@Param("key") 给参数添加注解的方式
     */
    public int insertStudent(Student student);
    public int deleteStudent(String stuNum);
    public int updateStudent(Student student);
    public List<Student> listStudents();
    public Student queryStudent(String stuNum);
//    public List<Student> listStudentsByPage(HashMap<String,Integer> map);
//    public List<Student> listStudentsByPage(int start,int pageSize);
    public List<Student> listStudentsByPage(@Param("start") int start
                                        ,@Param("pageSize") int pageSize);
 
    public int getCount();
 
}

在StudentMapper.xml配置sql时,使用 resultType 指定当前操作的返回类型为int

<select id="getCount" resultType="int">
    select count(1) from tb_students
</select>

单元测试

@Test
public void testGetStudentCount(){
    try{
        InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sqlSessionFactory = builder.build(is);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
        int i = studentDAO.getCount();
        System.out.println(i);
    }catch (IOException e){
 
    }
 
}

8:添加操作回填生成的主键

在StudentMapper.xml

在insert标签中 useGeneratedKeys 设置添加操作是否需要回填生成的主键 keyProperty 设置回填的位置

  <!--parameterType 这个参数在DAO中已经指定类型了,在这里可以省略-->
<!--    useGeneratedKeys 设置添加操作是否需要回填生成的主键-->
<!--    keyProperty 设置回填的位置-->
    <insert id="insertStudent" parameterType="com.liguoqing.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
        insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
        values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
    </insert>

六:MyBatis工具类封装

MyBatisUtil

package com.liguoqing.utils;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import java.io.IOException;
 
public class MyBatisUtil {
 
    private static SqlSessionFactory factory;//单例的
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
 
    static {
        try {
            //加载myBatis配置文件,创建会话工厂
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    //得到sqlSession对象
    public static SqlSession getSqlSession() throws IOException {
        SqlSession sqlSession = local.get();
        if (sqlSession == null){
            sqlSession = factory.openSession();
            local.set(sqlSession);
        }
        return sqlSession;
    }
 
    public static <T extends Object>T getMapper(Class<T> C) throws IOException {
        SqlSession sqlSession = getSqlSession();
        T dao = sqlSession.getMapper(C);
        return dao;
    }
 
    public static SqlSessionFactory getFactory() throws IOException {
        if (factory == null){
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        }
        return factory;
    }
 
 
}

增删改,需要commit

七:事务管理

sqlSession对象:

  • getMapper(DAO.class) 获取Mapper(DAO接口的实例)

  • 事务管理

1:手动提交事务

  • sqlSession .commit() 提交事务

  • sqlSession.rollback() 事务回滚

 @Test
    public void insertStudent() {
 
        try {
//            //加载mybatis配置文件
//            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//            //创建builder
//            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//            //会话工厂,连接工厂
//            SqlSessionFactory factory = builder.build(is);
//            //sqlsession 代表数据库的连接,也代表数据库的连接对象
//            //会话(连接)
//            SqlSession sqlSession = factory.openSession();
//            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//            System.out.println(studentDAO);
 
             SqlSession sqlSession = MyBatisUtil.getSqlSession();
             //1:当我们获取sqlSession对象时,就默认开启了事务
            try{
                StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
                Student student = new Student(0, "10005", "java少年", "男", 24);
                int i = studentDAO.insertStudent(student);
                //2:操作完成并成功以后,需要手动提交
                sqlSession.commit();
                System.out.println(student);
                System.out.println(i);
            }catch (Exception e){
                //3:当操作出现异常,调用rollback进行回滚
                sqlSession.rollback();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
 
 
    }

2:自动提交事务

sqlSession = factory.openSession(true);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务

MyBatisUtil优化

package com.liguoqing.utils;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import java.io.IOException;
 
public class MyBatisUtil {
 
    private static SqlSessionFactory factory;//单例的
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
 
    static {
        try {
            //加载myBatis配置文件,创建会话工厂
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    //得到sqlSession对象
    public static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
        SqlSession sqlSession = local.get();
        if (sqlSession == null){
            sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
            local.set(sqlSession);
        }
        return sqlSession;
    }
  // <T extends Object>T  表示字符T是一个类型      (Class<T> t)参数的类型Class 类型的泛型
    public static <T extends Object>T getMapper(Class<T> C) throws IOException {
        SqlSession sqlSession = getSqlSession(true);//自动提交
        T dao = sqlSession.getMapper(C);
        return dao;
    }
 
    public static SqlSessionFactory getFactory() throws IOException {
        if (factory == null){
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        }
        return factory;
    }
 
 
}

k

测试操作:

    @Test
    public void testDeleteStudent() {
        try {
 
//            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//            SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//            //SqlSessionFactory表示mybatis的会话工厂,工厂模式
//            SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is);
//            //SqlSession 对象是mybatis和数据库之间的连接,也就是会话,创建了连接,可以得到所有的mapper对象(映射关系)
//            SqlSession sqlSession = sqlSessionFactory.openSession();
//            //通过SqlSession 对象调用getMapper方法获取DAO接口对象
//            StudentDAO studentDAO = sqlSession.getMapper(StudentDAO.class);
//            //调用被测试方法
//            int i = studentDAO.deleteStudent("10001");
//            //提交事务
//            sqlSession.commit();
//            System.out.println(i);
            StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
            int i = studentDAO.deleteStudent("10001");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

当有多个操作一般都使用手动提交

MyBatisUtil 事务管理优化:

package com.liguoqing.utils;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import java.io.IOException;
 
public class MyBatisUtil {
 
    private static SqlSessionFactory factory;//单例的
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
 
    static {
        try {
            //加载myBatis配置文件,创建会话工厂
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
 
    //得到sqlSession对象
    private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
        SqlSession sqlSession = local.get();
        if (sqlSession == null){
            sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
            local.set(sqlSession);
        }
        return sqlSession;
    }
 
    //手动事务管理
    public static SqlSession getSqlSession() throws IOException {
        return getSqlSession(false);
    }
 
    //自动事务管理
    public static <T extends Object>T getMapper(Class<T> C) throws IOException {
        SqlSession sqlSession = getSqlSession(true);
        T dao = sqlSession.getMapper(C);
        return dao;
    }
 
    public static SqlSessionFactory getFactory() throws IOException {
        if (factory == null){
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        }
        return factory;
    }
 
 
}

八:MyBatis主配置文件

mybatis-config.xml 是MyBatis框架的主配置文件,主要用于配置MyBatis数据源以及工作属性信息

注意

  • 标签使用时需要注意顺序
  • 空标签会导致报错

1:properties标签

  • 可以定义键值对
  • 可以引用属性文件

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/49B7778AEFB5499D9D6ED7C397DF9CF3/12893

  • 在resource 文件夹下创建 jdbc.properties 文件,配置兼职对如下:
mysql_driver=com.mysql.jdbc.Driver
mysql_url=jdbc:mysql://localhost:3306/jdbcpractice?characterEncoding = utf-8
mysql_username=root
mysql_password=123456
  • 在mybatis-config.xml中通过properties 标签引用 jdbc.properties 文件,引入后 在配置environment时,可以直接使用jdbc.properties 的key获取value

2:settings标签

<!--    设置mybatis的属性-->
    <settings>
<!--        启动二级缓存-->
        <setting name="cacheEnable" value="true"/>
<!--        启用延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
    </settings>

3:typeAliases标签

<!--    typeAliases用于给实体类取别名,在映射文件中可以直接使用别名来替代实体类的全限定名-->
    <typeAliases>
        <typeAlias type="com.liguoqing.pojo.Student" alias="Student"></typeAlias>
    </typeAliases>

4:plugins标签

<!--    plugins主要用于配置MyBatis插件,例如分页插件-->
    <plugins>
        <plugin interceptor=""></plugin>
    </plugins>

5:environment

<!--  environments配置数据库连接信息  -->
<!--    environments中可以定义多个environments标签(正式测试预发布),每个environments可以定义一套数据库连接配置-->
<!--    default属性,用来执行使用哪个environment 标签,-->
    <environments default="mysql">
        <environment id="mysql">
<!--      transactionManager用于配置数据库管理方法     type="JDBC" 可以进行事务的提交和回滚操作,type="MANAGED" 事务的提交和回滚由容器进行控制 -->
            <transactionManager type="JDBC"></transactionManager>
<!--            dataSource标签用来配置数据库连接信息 POOLED|UNPOOLED 是否使用连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${mysql_driver}"/>
                <property name="url" value="${mysql_url}"/>
                <property name="username" value="${mysql_username}"/>
                <property name="password" value="${mysql_password}"/>
            </dataSource>
        </environment>
    </environments>

6:mappers标签

<!--    mappers用于载入映射文件,载入DAO类-->
    <mappers>
        <mapper resource="mappers/StudentMapper.xml"></mapper>
    </mappers>

九:映射文件

1:MyBatis初始化,可以跳进源码看一下

用到了动态代理

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/23AEBB7A6CCA4AB783C8A35362D52FC4/12952

2:Mappers根标签

mapper文件相当与DAO接口的实现类,namespace属性要执行实现DAO的全限定名(Copy Reference)

3:insert标签

public int insertStudent(Student student);

声明添加操作(sql: insert…)

常用属性:

  • id属性,绑定对应DAO中的方法

  • parameterType属性,用以指定接口中对应方法的参数类型(可省略)

  • useGeneratedKeys属性,用以设置添加操作是否需要回填生成的主键

  • keyProperty属性,指定回填的id设置到参数对应中的哪个字段中

  • timeout属性,设置超时时间,如果不设置会一直等待,如果设置了值并到时如果还没有执行,那么会报错

主键回填两种方式:

方式一:

<insert id="insertStudent" parameterType="com.liguoqing.pojo.Student" useGeneratedKeys="true" keyProperty="stuId">
    insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
    values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>

方式二:

<insert id="insertStudent">
    <selectKey keyProperty="stuId" resultType="java.lang.Integer">
        select  last_insert_id()
    </selectKey>
    insert into tb_students(stu_num, stu_name, stu_gender, stu_age)
    values (#{stuNum}, #{stuName}, #{stuGender}, #{stuAge})
</insert>

4:deleted标签

public int deleteStudent(String stuNum);
<delete id="deleteStudent">
    delete
    from tb_students
    where stu_num = #{stuNum}
</delete>

5:update标签

public int updateStudent(Student student);
<update id="updateStudent">
    update tb_students
    set stu_name=#{stuName},
        stu_gender=#{stuGender},
        stu_age=#{stuAge}
    where stu_num = #{stuNum}
</update>

6:select标签

public List<Student> listStudents();
select两种查询方式

查询要添加一个结果类型的标签 resultMap resultType

方式一:resultMap(推荐)
<resultMap id="studentMap" type="com.qfdue.pojo.Student">
    <id column="sid" property="stuId"/>
    <result column="stu_num" property="stuNum"/>
    <result column="stu_name" property="stuName"/>
    <result column="stu_gender" property="stuGender"/>
    <result column="stu_age" property="stuAge"/>
</resultMap>
<!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
    select sid,
           stu_num,
           stu_name,
           stu_gender,
           stu_age
    from tb_students
</select>


<select id="queryStudent" resultMap="studentMap">
    select sid,
           stu_num,
           stu_name,
           stu_gender,
           stu_age
    from tb_students
    where stu_num = #{stuNum}
</select>
方式二:resultType
<select id="listStudents" resultType="com.qfdue.pojo.Student">
    select sid as stuId, stu_num as stuNum, stu_name stuName, stu_gender stuGender, stu_age stuAge
    from tb_students
</select>

声明查询操作

  • id属性,指定绑定方法的方法名
  • resultType属性,期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
  • resultMap属性,对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
  • parameterType属性,将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
  • useCache属性,指定查询操作是否使用缓存
  • timeout属性,设置超时时间
  • … 其他的属性可以查看MyBatis文档

7:resultMap标签

<!--resultMap标签用于定义实体类和数据表的映射关系(ORM)-->
<resultMap id="studentMap"  type="Student">
    <id column="sid" property="stuId"/>
    <result column="stu_num" property="stuNum"/>
    <result column="stu_name" property="stuName"/>
    <result column="stu_gender" property="stuGender"/>
    <result column="stu_age" property="stuAge"/>
</resultMap>

8:cache标签

设置当前dao进行数据库操作时的缓存属性设置

<cache type="" size="" readOnly="false"></cache>

9:sql 和 include

1.未使用sql片段
<select id="listStudents" resultMap="studentMap">
    select sid,
           stu_num,
           stu_name,
           stu_gender,
           stu_age
    from tb_students
</select>
2.使用sql片段
<sql id="sqlpianduan">sid,
           stu_num,
           stu_name,
           stu_gender,
           stu_age</sql>
 
<!--    resultMap 用于引用一个实体的映射关系,当配置了resultMap以后,resultType就可以省略-->
<select id="listStudents" resultMap="studentMap">
    select <include refid="sqlpianduan"/>
    from tb_students
</select>

十:分页插件

分页插件是一个独立于MyBatis之外的第三方插件;

1:添加分压插件的依赖 —— PageHelper

<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.2.0</version>
</dependency>
 

2:配置插件

在MyBatis的主配置文件中mybatis-config.xml 中通过 plugins标签进行配置,注意标签的位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBkxOXQX-1651847136734)(https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/BE5DF1C76CB948A39AA6FB1734466745/13082)]

<!--    plugins主要用于配置MyBatis插件,例如分页插件-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
    </plugins>

3:分页实例

对学生信息进行分页查询

@Test
public void testGetStudentByPage() throws IOException {
    StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);//sqlSession
    PageHelper.startPage(1,4);//分页,从第1页开始,往后的4条数据
    //一定要在查询数据之前startPage, 相当设置一个拦截器
    List<Student> studentList = studentDAO.listStudents();
    //查全量就可以,分页组件会自动将数据进行分页
    PageInfo<Student> pageInfo = new PageInfo<Student>(studentList);
    //返回的时候只需要将pageInfo 返回就可以,因为pageInfo中就包含了分页以及数据的所有信息
}

带条件的分页

package com.liguoqing.dao;
 
import com.liguoqing.pojo.Student;
import org.apache.ibatis.annotations.Param;
 
import java.util.HashMap;
import java.util.List;
 
public interface StudentDAO {
 
    public List<Student> listStudentsByGender(String gender);
 
}
————————————————————————————————————————————————————————————————————————————————
<select id="listStudentsByGender" resultMap="studentMap">
    select <include refid="sqlpianduan"/>
        from tb_students where stu_gender = #{stuGender}
</select>
—————————————————————————————————————————————————————————————————————————————————
   @Test
    public void testGetStudentByPageGender() throws IOException {
        StudentDAO studentDAO = MyBatisUtil.getMapper(StudentDAO.class);
        PageHelper.startPage(1,4);
        List<Student> girls = studentDAO.listStudentsByGender("女");
        PageInfo<Student> pageInfo = new PageInfo<Student>(girls);
        //pageInfo中就包含了分页以及数据信息
        for(Student s : pageInfo.getList()){
            System.out.println(s);
        }
    }

十一:关联映射

1:实体关系

实体——数据实体,实体关系指的就是数据和数据之间的关系

例如:用户和角色,房屋和漏洞,订单和商品

实体关系分为以下四种:

1.1:一对一关联

实例:人和身份证、学生和学生证、用户基本信息和详情

数据表关系:

  • 主键关联(用户表主键和详情表主键相同时,表示是匹配的数据)

  • #用户信息表
    create table users(
        user_id int primary key auto_increment,
        user_name varchar(20) not null unique,
        user_pwd varchar(20) not null,
        user_realname varchar(20) not null,
        user_img varchar(100) not null
    );
     
    
    

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/E4C36455C6584C0FA7F38C08949EB56C/13152

  • 唯一外键关联

  •  
    #用户详情表
    create table details(
        detail_id int primary key auto_increment,
        user_addr varchar(50) not null,
        user_tel char(11) not null,
        user_desc varchar(200),
        uid int not null unique  
        #containt FK_USER foregic key (uid) references users(user_id)#这个设置是物理关联
        
        #并没有设置外键,外键的功能通过业务逻辑来实现
    );
    

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/03BCFEBF2E4949ACBD4038507089B76F/13160

1.2:一对多、多对一关联

实例:

  • 一对多:班级和学生、类别和商品

  • 多对一:学生和班级、商品和类别

数据表关系:

  • 在多的一端添加外键和一的一端进行关联
1.3:多对多关联

实例:用户和角色、角色和权限、房屋和业主、学生和社团、订单和商品

数据表关系:

建立第三张关系表添加两个外键,分别与两张表的主键进行关联

用户(user_id)——用户角色表(uid,rid) —— 角色(role_id)

2:创建项目,部署MyBatis框架

  • 创建web项目(Maven)

两种方式导入依赖

1.在idea的projectStucture–>modules导入tmocat依赖

image-20220419214827350

2.在pom文件中添加和tomcat有关的web依赖

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aODTALnv-1651847136739)(https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/DF280629F6994A93B5AEA5FE797EAE69/13206)]

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
 
</web-app>

添加依赖

<dependencies>
 
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
 
    <!-- https://mvnrepository.com/artifact/javax.servlet/jsp-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
        <scope>provided</scope>
    </dependency>
 
 
</dependencies>
  • 部署MyBatis框架

添加依赖

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
 
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

配置文件

帮助类

package com.guoqing.utils;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import java.io.IOException;
 
public class MyBatisUtil {
 
    private static SqlSessionFactory factory;//单例的
    private static final ThreadLocal<SqlSession> local = new ThreadLocal<SqlSession>(); //线程锁,各个线程之间不影响,例如开启关闭数据库连接之类的
 
    static {
        try {
            //加载myBatis配置文件,创建会话工厂
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
 
    //得到sqlSession对象
    private static SqlSession getSqlSession(boolean isAutoCommit) throws IOException {
        SqlSession sqlSession = local.get();
        if (sqlSession == null){
            sqlSession = factory.openSession(isAutoCommit);//这里的参数,如果不填,默认是false,也就是需要手动进行提交,如果填了true会自动提交事务
            local.set(sqlSession);
        }
        return sqlSession;
    }
 
    //手动事务管理
    public static SqlSession getSqlSession() throws IOException {
        return getSqlSession(false);
    }
 
    //自动事务管理
    public static <T extends Object>T getMapper(Class<T> C) throws IOException {
        SqlSession sqlSession = getSqlSession(true);
        T dao = sqlSession.getMapper(C);
        return dao;
    }
 
    public static SqlSessionFactory getFactory() throws IOException {
        if (factory == null){
            //加载主配置文件
            factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
        }
        return factory;
    }
 
 
}
2.1: 一对一关联

实例:用户和详情

创建数据表
#用户信息表
create table users(
    user_id int primary key auto_increment,
    user_name varchar(20) not null unique,
    user_pwd varchar(20) not null,
    user_realname varchar(20) not null,
    user_img varchar(100) not null
);
 
 
#用户详情表
create table details(
    detail_id int primary key auto_increment,
    user_addr varchar(50) not null,
    user_tel char(11) not null,
    user_desc varchar(200),
    uid int not null unique
);
 
创建实体类
  • User
 
<!--这里的@注解是通过lombook包帮我们实现的-->
<!--这里重写ToString 是为了打印-->
@Data  //让user有了get set方法
@NoArgsConstructor// 无参构造器
@AllArgsConstructor// 全参构造器
@ToString  //tostring方法
public class User {
    private int userId;
    private String userName;
    private String userPwd;
    private String userRealName;
    private String userImg;
}
  • Detail
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Detail {
    private int detailId;
    private String userAddr;
    private String userTel;
    private String userDesc;
    private int userId;
}
添加操作
package com.guoqing.dao;
 
import com.guoqing.pojo.User;
 
public interface UserDao {
    public int insertUser(User user);
}

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/58071327788041D8AFE597D140B6E925/13313

测试代码:

@Test
public void testInsertUser() throws IOException {
    //用户注册提交了基本信息和详情到Servlet,Servlet接收到注册信息,分装到User和Detail对象中
    User user = new User(0,"zhangsan6","123123","张三","01.jpg",null);
    Detail detail = new Detail(f);
 
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try{
 
        UserDao userDao = sqlSession.getMapper(UserDao.class);
        int i = userDao.insertUser(user);//添加用户
        System.out.println(i);
 
        detail.setUserId(user.getUserId());
        DetailDao detailDao = sqlSession.getMapper(DetailDao.class);
        int n = detailDao.insertDetail(detail);//添加详情
        System.out.println(n);
 
        sqlSession.commit();//提交事务
 
    }catch (Exception e){
        e.printStackTrace();
        sqlSession.rollback();//回滚事务
    }
 
}
一对一关联查询

在查询用户的同时关联查询出与之对应的详情

实体

(一对一要将Detail 类加入到user类中)

UserDetail
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/9D07299887AD44DEB0A8489DFFE03ECA/13331https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/AD7C0EC8E6D94936B457718BDCABB6F9/13328

映射文件

(要将detail 属性加入到user的result map标签中)

连接查询 inner join
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/6A6C624F605E46F4A8AF6664CA804685/13345
子查询
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/58A15802AF174201816207A4D4F37C6D/13352
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/DE29D2D16B2D4D049124D93545B4ED8B/13355

测试代码:

@Test
public void testQueryUser() throws IOException {
    UserDao userDao = MyBatisUtil.getMapper(UserDao.class);
    User user = userDao.queryUser("zhangsan3");
    System.out.println(user);
}
2.2: 一对多关联

案例:班级(一)——学生(多)

创建数据表
#创建班级信息表
CREATE TABLE classes(
cid int PRIMARY key auto_increment,
cname VARCHAR(30) not null unique,
cdesc VARCHAR(100)
);
 
#创建学生信息表
CREATE TABLE students(
sid CHAR(5) PRIMARY key,
sname VARCHAR(20) not null,
sage int not null,
scid int not null 
);
创建实体类
ClazzStudent
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/C015A157FB624A5283262AD5C0E48E80/13395https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/73682350ED41461CA83DC26D2B5506C9/13389
关联查询

当查询一个班级的时候,要关联查询出这个班级下的所有学生

连接查询

连接查询的映射配置
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/5E18924F52C9425F853CA80996A6AFFB/13399

子查询

注意mapper与主配置文件的关系以及变量重命名之间的配置

子查询的映射配置
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/58ECF9F1CBB540FAACABDE5146020132/13404
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/0343DB44EC4442B489899F66CF174B17/13406
2.3:多对一关联

实例:学生(n) ——班级(1)

当查询一个学生的时候,关联查询这个学生所在的班级信息

创建实体类
StudentClazz
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/F5CD756559BF48ABAB32A9CDEE215F1B/13461https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/B99AE6AE8F9542488B64A71ADA137DEC/13463
关联查询

连接查询

连接查询的映射配置
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/FAE7936DCB354C9D94EF9F89D7D85022/13471

子查询

子查询映射配置
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/9F5AC67155564C54A9AC527F7ED4536B/13479
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/5C56B21C8FFD4478ADB0F1473E03482B/13481

image-20220420205556180

单个对象association

2.4:多对多关联

案例:学生(m)——课程(n)

创建数据表
#学生信息表如上
#课程信息表
CREATE TABLE courses(
 course_id int PRIMARY key auto_increment,
 course_name VARCHAR(50) not null
);
 
#选课信息表/成绩表(学号,课程号,成绩)
CREATE TABLE grades(
	sid CHAR(5) not null,
	cid int not null,
	score int not null
);
关联查询

查询学生时,同时查询出学生选择的课程

StudentCourse
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/DF3C37D39BCD463F9F5709E804E434CA/13504https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/9650A6959C3A4E8991C371E8B154047A/13507

根据课程编号查询课程时,同时查询选择了这门课程的学生

StudentCourse
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8wm0OKl-1651847139600)(https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/8AA75848E9BC4C0794814E9336033B4D/13514)]https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/EF84CED272454309BF3FEA0AA4697E76/13516
连接查询映射配置
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/C30A749848414C2EB6D955A51F88F423/13520
子查询映射配置
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/0EB58F7A5FA844D7B39D7CFBEFB3AC68/13527
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/F1464E233A57410CA711EB88911BCD45/13529

十二:动态SQL

快速构建一个项目

image-20220421100108206
image-20220421100229800
image-20220421100211739
image-20220421100310302
image-20220421100802908
image-20220421100406187

交友网;电商等网站都有筛选功能

不同的需求筛选条件不同,需要动态的拼接sql

用户的筛选条件不同,我们完成筛选执行的sql也不一样,我们可以通过穷举来意义完成不同条件的筛选,但是这种实现思路过于繁琐和复杂,MyBatis就提供了动态SQL的配置方式来实现多条件的查询。

12.1 什么是动态SQL

根据搜索条件,动态完成SQL的拼接

12.2动态SQL使用案例

案例:心仪对象搜索

12.2.1 创建数据表
#数据表
CREATE TABLE members(
	member_id int PRIMARY key auto_increment,
	member_nick VARCHAR(20) not null UNIQUE,
	member_gender char(2) not null,
	member_age int not null,
	member_city varchar(30) not null
);
12.2.2 创建实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member {
    private int memberId;
    private String memberNick;
    private String memberGender;
    private int memberAge;
    private String memberCity;
}
12.2.3 创建DAO接口

在DAO接口中定义一个多条件查询的方法

public interface MemberDAO {
    //在多条件查询中,如果查询条件不确定,可以直接使用HashMap作为参数
    public List<Member> searchMember(HashMap<String,Object> params);
}
——————————MemberMapper.xml
<resultMap id="memberMap" type="Member">
    <id column="member_id" property="memberId"/>
    <result column="member_nick" property="memberNick"/>
    <result column="member_gender" property="memberGender"/>
    <result column="member_age" property="memberAge"/>
    <result column="member_city" property="memberCity"/>
</resultMap>
 
<select id="searchMember" resultMap="memberMap">
    select member_id,member_nick,member_gender,member_age,member_city
    from members
    where 1 = 1
    <if test="gender != null"><!--gender 就是参数对象的属性、参数Map的key-->
        and member_gender = #{gender}
    </if>
    <if test="minAge != null"><!--&gt; 大于号  -->
        and member_age &gt;= #{minAge}
    </if>
    <if test="maxAge != null"><!--&lt; 小于号  -->
        and member_age &lt;= #{maxAge}
    </if>
    <if test="city != null">
        and member_city != #{city}
    </if>
</select>
 
 

-----MemberDAOTest
   @Test
    public void TestSearchMember() throws IOException {
        HashMap<String,Object> params = new HashMap<String,Object>();
        params.put("gender","女");
//        params.put("minAge",19);
//        params.put("maxAge",23);
//        params.put("city","武汉");
        //当向Map中存放参数时,key必须与动态Sql中的参数保持一致
        MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
        List<Member> members = memberDAO.searchMember(params);
 
        System.out.println(members);
 
    }
if标签

image-20220421102541977

12.2.4 where标签(就可以去掉where 1=1)
image-20220421110718009
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/3AE5CBAA422446EB959ACF42BC28B829/13576
image-20220421112702883
12.2.5 trim标签

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bKskJW8k-1651847136744)(https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/9C3C807BA7D94E2D97B27D5E7CC20192/13579)]

12.2.6 foreach标签 (在in语句后面)
https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/B81269D3898B41EA99B708CD77B1F830/13582
image-20220421122349797

DAO类:

 List<Member> searchMemberByCity(List<String> cities);

测试类:

@Test
public void testSearchMemberByCity() throws IOException {
    List<String> cities = new ArrayList<String>();
    cities.add("武汉");
    cities.add("厦门");
    MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
    List<Member> members = memberDAO.searchMemberByCity(cities);
    for (Member m : members){
        System.out.println(m);
    }
}

十三:模糊查询

案例:根据昵称查询会员信息(模糊匹配 like)

13.1 模糊查询实现

13.1.0使用(@Param(“keyWord”) String keyWord)
13.1.1 DAO
public interface MemberDAO {
 
//根据昵称查询用户信息-- 模糊查询
//模糊查询需要使用 ${} 取值,与SQL进行拼接
//在使用${}时,即使只有一个参数也需要使用@Param注释声明参数的key (非String类型不需要)
    public List<Member> searchMemberByNick(@Param("keyWord") String keyWord);
 
}
13.1.2 映射文件
 
<!--如果参数是String 类型,需要使用parameterType声明参数类型-->
<select id="searchMemberByNick" parameterType="java.lang.String" resultMap="memberMap">
    select member_id,member_nick,member_gender,member_age,member_city
    from members
    where member_nick like '%${keyWord}%'
</select>
 

image-20220421173249126

13.1.3 测试
    @Test
    public void testSearchMemberByNick() throws IOException {
        MemberDAO memberDAO = MyBatisUtil.getMapper(MemberDAO.class);
        List<Member> members = memberDAO.searchMemberByNick("x");
        for (Member m : members){
            System.out.println(m);
        }
    }
13.2使用(HashMap keyword)
image-20220421172719039
image-20220421172753039
image-20220421172816816
image-20220421172845934
13.2 #{} 和${}的区别
<!--${keyWord} 表示获取参数,先获取参数的值,拼接到SQL语句中,再编译执行SQL语句  可能引起SQL注入问题-->

sql注入:sql参数的值可能会导致原意发生变化

<!--#{keyWord} 表示获取参数,先完成SQL的编译(预编译),预编译之后再将获取的参数设置到SQL中 可以避免SQL注入问题-->

十四:MyBatis日志配置

MyBatis 做为一个封装好的ORM框架,其运行过程我们没办法跟踪,为了让开发者了解MyBatis执行流程及每个执行步骤所完成的工作,MyBatis框架本身集成了log4j日志框架(这两天有漏洞),对运行的过程进行跟踪记录,我们只需对MyBatis进行相关的日志配置,就可以看到MyBatis运行过程中的日志信息。-

14.1 添加日志框架依赖

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

14.2 添加日志配置文件

  • 在 resources目录下创建名为 log4j.properties 文件,名称必须如此

  • 在log4j.properties 文件中配置日志输出的方式

#声明日志的输出级别及输出格式
log4j.rootLogger=DEBUG,stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output ...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
#定义日志的打印格式    %t表示 线程名称    %5p 日志级别 %msg 日志信息  \:%m%n 换行
log4j.appender.stdout.layout.ConversionPattern=[%t] %5p -%msg \:%m%n

14.3 日志信息的级别

在使用日志框架输出日志信息的时候,会根据输出的日志信息的重要程度分为5个级别

级别说明
DEBUG调试
INFO提示
WARN警告
ERROR一般
FATAL致命

十五:配置数据库连接池-整合Druid

MyBatis作为一个ORM框架,在进行数据库操作时是需要和数据库连接的,MyBatis支持基于数据库连接池的连接创建方式。

当我们配置MyBatis数据源时,只要配置了dataSource标签的type属性值为POOLED时,就可以使用MyBatis内置的连接池管理连接。

如果我们想要使用第三方的数据库连接池,则需要进行自定义配置。

15.1 常见的连接池

  • DBCP

  • C3P0

  • Druid 性能比较好,提供了比较便捷的监控系统

  • Hikari 性能最好

功能dbcpdruidc3p0HikariCP
是否支持PSCache
监控jmxjmx/log/httpjmx,logjmx
扩展性
sql拦截及解析支持
代码简单中等复杂简单
更新时间2015.8.62015.10.102015.12.092015.12.3
特点依赖于common-pool阿里开源,功能全面历史久远,代码逻辑复杂,且不易维护优化力度大,功能简单,起源于boneCP
连接池管理LinkedBlockingDeque数组threadlocal+CopyOnWriteArrayList

15.2 添加Druid依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

15.3 创建Druid连接池工厂

public class DruidDataSourceFactory extends PooledDataSourceFactory {
    // protected DataSource dataSource = new UnpooledDataSource();

    
//    @Override
//    public DataSource getDataSource() {
//        return super.getDataSource();
//    }
    public DruidDataSourceFactory() {
        this.dataSource=new DruidDataSource();
    }
}

15.4 将DruidDataSourceFactory 配置给MyBatis数据源

<environments default="mysql">
        <environment id="mysql">
            <transactionManager type="jdbc"></transactionManager>
            <!--POOLED 使用MyBatis内置的连接池实现   -->
            <!--mybatis 需要一个连接池工厂  这个工厂可以产生数据库连接池 PooledDataSourceFactory -->
            <!--使用了多态-->
            <dataSource type="com.guoqing.utils.DruidDataSourceFactory">
                <property name="driverClass" value="${driver}"/>
                <property name="jdbcUrl" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>

十六,MyBatis缓存

MyBatis是基于JDBC的封装,使数据库操作更加便捷MyBatis除了对JDBC操作步骤进行封装之外也对其性能进行了优化

  • 在MyBatis引入了缓存机制,用于提升MyBatis的检索效率

  • 在MyBatis引入延迟加载机制,用于对数据库不必要的访问

16.1 缓存的工作原理

缓存,就是存储数据的内存

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/5DDF0329539A4A14AA3D1F192257942E/13739

16.2 MyBatis缓存

MyBatis缓存分为一级缓存和二级缓存

16.2.1 一级缓存

一级缓存也叫作Sqlsession缓存,为每个sqlsession单独分配的缓存内存,无需手动开启,可直接使用。多个sqlsession的缓存是不共享的。

特性:

1:如果多次查询使用的是同一个Sqlsession对象,则第一次查询以后,数据会存放到缓存,后续的查询则直接访问缓存中存储的数据。

 
    @Test
    public void testQueryMemberById() throws IOException {  
         /*(因为有localTread  在同一个线程中sqlSession 可以共享)
         
         getSqlSession()中的方法
         public static SqlSession getSqlSession(boolean isAutoCommit){
         SqlSession sqlSession = local.get();
         if (sqlSession==null){
              sqlSession = factory.openSession(isAutoCommit);
              local.set(sqlSession);
         }
         return sqlSession;
     }
     sqlSession是在同一个local中的是同一个sqlsession
         */
        SqlSession sqlSession = MyBatisUtil.getSqlSession();       
        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO.queryMemberById(1);
        System.out.println(member1);
        //sqlSession.clearCache();
  
        
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
 		SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
        MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
        Member member2 =memberDAO2.queryMemberById(1);
        System.out.println(member2);
    }

2:在同一个SqlSession对象的情况下,如果第一次查询完成后,对查询出的对象进行修改(此修改会影响到缓存),第二次查询会直接访问缓存,造成第二次查询的结果与数据库不一致。

image-20220421220041241

3:当我们进行再次查询时,想要跳过缓存直接查询数据库,则可以通过sqlsession.clearCache();来清除当前SqlSession的缓存。

@Test
    public void testQueryMemberById() throws IOException {  
         /*(因为有localTread  在同一个线程中sqlSession 可以共享)
         
         getSqlSession()中的方法
         public static SqlSession getSqlSession(boolean isAutoCommit){
         SqlSession sqlSession = local.get();
         if (sqlSession==null){
              sqlSession = factory.openSession(isAutoCommit);
              local.set(sqlSession);
         }
         return sqlSession;
     }
     sqlSession是在同一个local中的是同一个sqlsession    
     但是后面有一个sqlSession.clearCache();方法将sqlSession缓存清空了
         */
        SqlSession sqlSession = MyBatisUtil.getSqlSession();       
        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO.queryMemberById(1);
        System.out.println(member1);
        sqlSession.clearCache();
        
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
 		SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
        MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
        Member member2 =memberDAO2.queryMemberById(1);
        System.out.println(member2);
    }

4:如果第一次查询以后,第二次查询以前,使用当前的sqlsession执行了修改操作,此修改操作会使第一次查询并缓存的数据失效,(查询不会) 因此第二次查询会再次访问数据库。

image-20220421221707730

测试代码:

1.同一个sqlSession的测试

    @Test
    public void testQueryMemberById() throws IOException {  
         /*(因为有localTread  在同一个线程中sqlSession 可以共享)
         
         getSqlSession()中的方法
         public static SqlSession getSqlSession(boolean isAutoCommit){
         SqlSession sqlSession = local.get();
         if (sqlSession==null){
              sqlSession = factory.openSession(isAutoCommit);
              local.set(sqlSession);
         }
         return sqlSession;
     }
     sqlSession是在同一个local中的是同一个sqlsession
         */
        SqlSession sqlSession = MyBatisUtil.getSqlSession();       
        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO.queryMemberById(1);
        System.out.println(member1);
        //sqlSession.clearCache();
  
        
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
 		SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
        MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
        Member member2 =memberDAO2.queryMemberById(1);
        System.out.println(member2);
    }

2.不同sqlSession的情况

@Test
    public void testQueryMemberById() throws IOException {  
         /*(因为有localTread  在同一个线程中sqlSession 可以共享)
         
         getSqlSession()中的方法
         public static SqlSession getSqlSession(boolean isAutoCommit){
         SqlSession sqlSession = local.get();
         if (sqlSession==null){
              sqlSession = factory.openSession(isAutoCommit);
              local.set(sqlSession);
         }
         return sqlSession;
     }
     sqlSession是在同一个local中的是同一个sqlsession    
     但是后面有一个sqlSession.clearCache();方法将sqlSession缓存清空了
         */
        SqlSession sqlSession = MyBatisUtil.getSqlSession();       
        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO.queryMemberById(1);
        System.out.println(member1);
        sqlSession.clearCache();
        
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
 		SqlSession sqlSession2 = MyBatisUtil.getSqlSession();
        MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
        Member member2 =memberDAO2.queryMemberById(1);
        System.out.println(member2);
    }

 
    @Test
    public void testQueryMemberById() throws IOException {
        /*
         static {
         try {
             InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
             factory = new SqlSessionFactoryBuilder().build(is);
         } catch (IOException e) {
             e.printStackTrace();
         }
     }

     public static SqlSessionFactory getFactory(){
         return factory;
     }
     
     MyBatisUtil.getFactory().openSession();该方法创建的sqlSession是不同的
        */
        SqlSession sqlSession = MyBatisUtil.getFactory().openSession();
       
 
        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO.queryMemberById(1);
        System.out.println(member1);
        
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        SqlSession sqlSession2 = MyBatisUtil.getFactory().openSession();
        MemberDAO memberDAO2 = sqlSession.getMapper(MemberDAO.class);
        Member member2 =memberDAO2.queryMemberById(1);
        System.out.println(member2);
    }
16.2.2 两次查询与数据库数据不一致问题

https://note.youdao.com/yws/public/resource/7e6936ecee33a2e46f43f44eb1a178c8/xmlnote/AB1D085432E94B48A92F130B122028F9/13790

16.2.3 二级缓存

二级缓存也称为SqlSessionFactory级缓存。通过同一个factory对象获取的SqlSession 可以共享二级缓存;

在应用服务器中SqlSessionFactory是单例的(Factory只创建了一次在整个项目中),因此我们二级缓存可以实现全局共享。

特性:

1:二级缓存默认没有开启,需要在mybatis-config.xml中的setting标签中开启

2:二级缓存只能缓存实现了序列化接口的对象

  • 在mybatis-config.xml 开启使用二级缓存
<settings>
    <setting name="cacheEnabled" value="true"/>
</settings>
  • 在需要使用二级缓存的Mapper文件中配置cache标签使用二级缓存
<cache/>  
 
//该标签中有一些属性可以自行进行配置
//例如
淘汰策略属性: eviction = "FIFO"  先进先出
更新频率属性: flushInterval = "6000" 间隔多长时间刷新一次缓存
缓存区的大小属性:size = "223" 多少个对象的引用
只读属性: readOnly = "true" 只能读不能改
  • 被缓存的实体类实现序列化接口
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Member implements Serializable {
    private int memberId;
    private String memberNick;
    private String memberGender;
    private Integer memberAge;
    private String memberCity;
}
  • 测试

  • 1:多个sqlsession对象来自于同一个sqlsessionFactory

@Test
    public void testQueryMemberById() throws IOException {
        SqlSessionFactory factory = MyBatisUtil.getFactory();
        //1:多个sqlsession对象来自于同一个sqlsessionFactory
        SqlSession sqlSession = factory.openSession(true);
        SqlSession sqlSession2 = factory.openSession(true);
 
        MemberDAO memberDAO = sqlSession.getMapper(MemberDAO.class);
        Member member1 = memberDAO.queryMemberById(1);
        System.out.println(member1);
//        sqlSession.clearCache();
        sqlSession.commit(); //2:第一次查询之后,执行sqlSession.commit() 会将当前sqlsession的查询结果缓存到二级缓存
 
        System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
 
        MemberDAO memberDAO2 = sqlSession2.getMapper(MemberDAO.class);
        Member member2 =memberDAO2.queryMemberById(1);
        System.out.println(member2);
    }

16.3 查询操作的缓存开关和修改是是否刷新缓存

<select id="queryMemberById" resultMap="memberMap" useCache="false">
    select member_id,member_nick,member_gender,member_age,member_city
    from members
    where member_id=#{mid}
</select>

修改

    <update id="updateMemberByNick" flushCache="true">
        update members
        set member_nick=#{memberNick},
            member_gender=#{memberGender},
            member_age=#{memberAge},
            member_city=#{memberCity}
        where member_nick = #{memberNick}
    </update>

十七: 延迟加载

延迟加载——如果在MyBatis中开启了延迟加载执行了子查询(至少查询两次以上),默认只实行第一次查询,当用到子查询的查询结果时,才会出发子查询的执行,如果无需使用子查询结果,则子查询不会执行。

开启延迟加载

fetchType="lazy"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guoqing.dao.ClassDAO">
 
    <resultMap id="classMap" type="Clazz">
        <id column="cid" property="classId"/>
        <result column="cname" property="className"/>
        <result column="cdesc" property="classDesc"/>
        <collection property="stus" select="com.guoqing.dao.StudentDAO.queryStudentsByCid" column="cid" fetchType="lazy"/>
    </resultMap>
 
    <select id="queryClassByCid" resultMap="classMap">
        select cid,cname,cdesc from classes where cid = #{cid}
    </select>
 
</mapper>

测试类

@Test
public void queryClassByCid() throws IOException {
    ClassDAO classDAO = MyBatisUtil.getMapper(ClassDAO.class);
    Clazz clazz = classDAO.queryClassByCid(1);
    System.out.println(clazz.getClassName());
    System.out.println(clazz.getStus());
}

————————————————————————

总结

Map

  • 需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。

预编译

1、什么是预编译
1.1、 sql的执行过程

① 词法和语义分析
② 优化sql语句,指定执行计划
③ 执行并返回结果

我们把这种普通语句称作Immediate Statements。

select colume from table where colume=1;
select colume from table where colume=2;

但是很多情况,我们的一条sql语句可能会反复执行,或者每次执行的时候只有个别的值不同,那么这个时候会对上面两条语句生成两个执行计划,一千次查询就需要一千个执行计划,生成执行计划非常耗费时间。

如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了,所以引出预编译功能。

1.2. 预编译语句

将这类语句中的值用占位符替代,可以视为将sql语句模板化或者说参数化,一般称这类语句叫Prepared Statements或者Parameterized Statements。

比如下面的语句

insert into category values(null,?,?)
1.3. 预编译

​ 以java为例,预编译会使用preparestatement,会对预编译语句进行语法分析,编译。
那么这个时候会对上面的insert语句按照预编译语句生成一个执行计划,然后根据参数可以进行重用执行计划。

当处理批量SQL语句时,这个时候就可以体现PrepareStatement的优势,由于采用Cache机制,则预先编译的语句,就会放在Cache中,下次执行相同SQL语句时,则可以直接从Cache中取出来,效率要比statement高好几倍

​ 当语句真正开始执行时,传过来的参数只会被看成纯文本,不会重新编译,不会被当做sql指令,所以可以防止注入。

​ 很多时候最优的执行计划不是光靠知道sql语句的模板就能决定了,往往就是需要通过具体值来预估出成本代价。

​ 注意MySQL的老版本(4.1之前)是不支持服务端预编译的,但基于目前业界生产环境普遍情况,基本可以认为MySQL支持服务端预编译。

一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止sql注入。

varchar和char的区别

1.char定长类型
    char(M),M代表宽度即可容纳的字符数(如,char(4),这里面可以存4个字母或者四个汉字 ) 如果存了N(N<=M)个字符,实占 M 个字符,不足M,向后补空格,取出时,去除后面的空格。
    varchar(M) 变长类型varchar(M),M代表宽度,即可容纳的 字符 数如果存了N(N<=M)个字符,实占 N 个字符
2.     char 的速度比 varchar 快,但比 varchar 浪费空间
3.     char 的 M 的范围(0-255)  varchar 的 M 范围(0-65535)(约2W-6W个字符受字符集影响)
4.     char 和 varchar 的选择原则:
                1.空间利用效率
                        四字成语表:char(4)
                        个人简介,微博 140 字:varchar(140)

                2.速度
                        用户名:char
             根据实际情况具体衡量,兼顾空间和速度。   

xml文件的标准样式

image-20220419160756621

static,final,TreadLocal

(一)static

1static修饰的方法和属性会统一交给类管理,即多个类对象只会对应一个属性或方法,并由JVM分配在堆中的一块不变的区域。

2static修饰的方法,可以使用"类名.方法名"的方式调用,避免了需要new对象的繁琐和内存资源的消耗。

 

总结

1、用来修饰类成员变量,从而实现所有对象对成员变量的共享。

2、用来修饰成员方法,可以直接“类名.方法名”的方式调用,常用于工具类。

3、静态块用法,将多个类成员统一初始化,即在实际被调用之前就被初始化了,且只需要初始化一次。

4、静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”进行调用,更加方便。

 

(二)final

1、修饰成员变量,说明该变量的值是不可变得。标注该变量只能进行一次赋值操作,且在运行过程中不可改变它的值。

2、修饰方法入参,说明整个方法中,参数的值是不可变的。

注意:当入参为对象时,是可以改变引用对象中成员变量的值。

3、修饰方法,说明该方法不能被覆盖,即不能被继承该类的子类重写。

4、修饰类,说明该类是无法被继承的。

 

(三)ThreadLocal

1ThreadLocal为每个使用该变量的线程提供独立的变量副本,因此每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

2、解决多线程并发访问同一变量造成脏数据的问题。

 

提供的方法:
pub void set(Object value) 设置当前线程的线程局部变量的值
public Object get() 该方法返回当前线程所对应的线程局部变量的值
public void remove() 将当前线程局部变量的值删除,目的是为了内存的占用

静态代码块

还记得静态代码块的特点吗?随着类的加载而执行,而且只执行一次

静态代码块:执行优先级高于非静态的初始化块,它会在类初始化的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员。

mybatis工具类讲解

package com.ara.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * Mybatis工具类
 * 主要用于获取SqlSession
 */
public class MybatisUtil {

    //私有静态 加载就被执行 让其只存在一份在内存中
    private static SqlSessionFactory sqlSessionFactory;

    //通过静态代码块来实例化SqlSessionFactory
    static {
        try {
            //读取Mybatis的配置文件
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
            //实例化SqlSessionFactory 
            //因为SqlSessionFactoryBuilder在创建SqlSessionFactory之后就再也不会使用了,所以我们这里直接使用匿名对象创建SqlSessionFactory
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取SqlSession对象
     * 值得注意的是:获取的SqlSession对象在使用完毕之后应该立即关闭
     *
     * @return 返回SqlSession对象
     */
    public static SqlSession getSqlSession() {
        //通过SqlSessionFactory创建SqlSession供其他程序使用
        return sqlSessionFactory.openSession();
    }
}

studentmapper.xml

        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--类似创建了一个实现类-->
<mapper namespace="com.qfdue.dao.StudentDAO">
    <!--类似这个实现类的方法,但是这些方法的id要和StudentDAO的方法名一致-->
    <insert id="insertStudent" parameterType="com.qfdue.pojo.Student">

全类名复制

image-20220418203522657

resources文件夹

image-20220418201751264

如何快速得到文件的名称

image-20220418194740692

jdk

image-20220418161134181

环境变量

image-20220418161457047

<cache/>  
 
//该标签中有一些属性可以自行进行配置
//例如
淘汰策略属性: eviction = "FIFO"  先进先出
更新频率属性: flushInterval = "6000" 间隔多长时间刷新一次缓存
缓存区的大小属性:size = "223" 多少个对象的引用
只读属性: readOnly = "true" 只能读不能改
         1.空间利用效率
                    四字成语表:char(4)
                    个人简介,微博 140 字:varchar(140)

            2.速度
                    用户名:char
         根据实际情况具体衡量,兼顾空间和速度。   



### xml文件的标准样式

[外链图片转存中...(img-jzsUCAlE-1651847136752)]

### static,final,TreadLocal

```java
(一)static

1、static修饰的方法和属性会统一交给类管理,即多个类对象只会对应一个属性或方法,并由JVM分配在堆中的一块不变的区域。

2、static修饰的方法,可以使用"类名.方法名"的方式调用,避免了需要new对象的繁琐和内存资源的消耗。

 

总结

1、用来修饰类成员变量,从而实现所有对象对成员变量的共享。

2、用来修饰成员方法,可以直接“类名.方法名”的方式调用,常用于工具类。

3、静态块用法,将多个类成员统一初始化,即在实际被调用之前就被初始化了,且只需要初始化一次。

4、静态导包用法,将类的方法直接导入到当前类中,从而直接使用“方法名”进行调用,更加方便。

 

(二)final

1、修饰成员变量,说明该变量的值是不可变得。标注该变量只能进行一次赋值操作,且在运行过程中不可改变它的值。

2、修饰方法入参,说明整个方法中,参数的值是不可变的。

注意:当入参为对象时,是可以改变引用对象中成员变量的值。

3、修饰方法,说明该方法不能被覆盖,即不能被继承该类的子类重写。

4、修饰类,说明该类是无法被继承的。

 

(三)ThreadLocal

1、ThreadLocal为每个使用该变量的线程提供独立的变量副本,因此每一个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。

2、解决多线程并发访问同一变量造成脏数据的问题。

 

提供的方法:
pub void set(Object value) 设置当前线程的线程局部变量的值
public Object get() 该方法返回当前线程所对应的线程局部变量的值
public void remove() 将当前线程局部变量的值删除,目的是为了内存的占用

静态代码块

还记得静态代码块的特点吗?随着类的加载而执行,而且只执行一次

静态代码块:执行优先级高于非静态的初始化块,它会在类初始化的时候执行一次,执行完成便销毁,它仅能初始化类变量,即static修饰的数据成员。

mybatis工具类讲解

package com.ara.utils;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * Mybatis工具类
 * 主要用于获取SqlSession
 */
public class MybatisUtil {

    //私有静态 加载就被执行 让其只存在一份在内存中
    private static SqlSessionFactory sqlSessionFactory;

    //通过静态代码块来实例化SqlSessionFactory
    static {
        try {
            //读取Mybatis的配置文件
            InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
            //实例化SqlSessionFactory 
            //因为SqlSessionFactoryBuilder在创建SqlSessionFactory之后就再也不会使用了,所以我们这里直接使用匿名对象创建SqlSessionFactory
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取SqlSession对象
     * 值得注意的是:获取的SqlSession对象在使用完毕之后应该立即关闭
     *
     * @return 返回SqlSession对象
     */
    public static SqlSession getSqlSession() {
        //通过SqlSessionFactory创建SqlSession供其他程序使用
        return sqlSessionFactory.openSession();
    }
}

studentmapper.xml

        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--类似创建了一个实现类-->
<mapper namespace="com.qfdue.dao.StudentDAO">
    <!--类似这个实现类的方法,但是这些方法的id要和StudentDAO的方法名一致-->
    <insert id="insertStudent" parameterType="com.qfdue.pojo.Student">

全类名复制

[外链图片转存中…(img-VDUKSwG5-1651847136753)]

resources文件夹

[外链图片转存中…(img-ZbdL202T-1651847136754)]

如何快速得到文件的名称

[外链图片转存中…(img-bolF63ag-1651847136755)]

jdk

[外链图片转存中…(img-ysYul8Lg-1651847136756)]

环境变量

[外链图片转存中…(img-gxsBtNyS-1651847136757)]

<cache/>  
 
//该标签中有一些属性可以自行进行配置
//例如
淘汰策略属性: eviction = "FIFO"  先进先出
更新频率属性: flushInterval = "6000" 间隔多长时间刷新一次缓存
缓存区的大小属性:size = "223" 多少个对象的引用
只读属性: readOnly = "true" 只能读不能改
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值