MyBatis的基础学习以及基本使用

MyBatis

  • 1.简介

  MyBatis是apache下的一个开源框架iBatis,后来被更名为MyBatis,它是一款基于java基础的持久层框架,支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的过程,它可以使用简单的XML或注解来配置和映射相关类,将接口和Java的POJOs映射成数据库中的记录,也就是将数据库中的数据通过实体类进行封装存储。

  • 2.MyBatis的执行流程:(使用db_school中的tb_student来举例)

  (1)读取MyBatis核心配置文件(mybatis-config.xml : 核心配置文件一般在resources下,后面会记录该文件的配置)

//利用Resources的getResourceAsStream读取mybatis核心配置文件,该配置文件中注册数据源(dataSource)和映射文件(mappers标签中的子标签mapper中的resources属性)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

 

 

  (2)加载映射文件StudentMapper.xml (路径:resources下的com.hpc.mapper.StudentMapper.xml)

//例如映射文件中,查询的是Student的所有信息;加载的是StudentDao.xml中的文件
<mapper namespace="com.hpc.mapper.StudentMapper>
    <select id="getAllStudent" resultType="com.hpc.pojo.Student">
        select * from tb_student;
    </select>
</mapper>

 

  (3)构造会话工厂

//使用构造者模式SqlSessionFactoryBuilder类的build方法创建SqlSessionFactory对象,
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//在build方法中从字节输入流in中解析数据
SqlSessionFactory factory = builder.build(in);

 

  (4)创建会话对象SqlSession,调用SqlSessionFactory对象的openSession()方法返回会话对象SqlSession

SqlSession sqlSession = factory.openSession();

 

  (5)Executor执行器。是MyBatis的核心,负责SQL语句的生成和查询缓存的维护,将SqlSession传递的参数动态地生成需要执行的SQL语句,并负责查询缓存的维护。

  (6)创建代理对象。映射器接口的实例是从SqlSession中获得的。

StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class)

 

  (7)执行编写的SQL语句,并返回数据。(这儿查询的是所有学生对象,所以返回的是List的学生对象集合)

List<Student> list = studentMapper.getAllStudent();//调用Mapper层的方法;
list.forEach(System.out::println);//遍历输出所有的学生对象;

 

  (8)释放资源

sqlSession.close();
in.close();

 

下图是MyBatis的大体的执行流程图:

 

  • 3.Mybatis的基本使用(使用的是maven来构建项目)

  (1)在pom.xml文件中引入MyBatis的依赖及设计到的关于数据库的包

<!--Mybatis相关依赖-->
<dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.1</version>
</dependency>

<!--数据库驱动相关依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
<scope>runtime</scope>
</dependency>

<!--单元测试相关依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>

<!--lombok相关依赖:方便实体类的书写,用注解方式完成-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>

  (2)数据库的配置文件:db.properties存放在resources文件夹下,mysql8的驱动包多了一个cj,allowMultiQueries=true:开启批量执行sql参数(如果一次只执行单条sql语句,那么可以不用写)

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_school?allowMultiQueries=true
jdbc.username=root
jdbc.password=111

  (3)MyBatis的使用肯定是与数据库相关的,所以要配置MyBatis关于数据库连接的核心文件配置,同时还有其他涉及到的文件进行配置;(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>
   <properties resource="db.properties">
    </properties>
   <!-- 配置 mybatis的环境 --> <environments default="development"> <!-- 配置环境 --> <environment id="development"> <!-- 配置事务的类型 --> <transactionManager type="JDBC"></transactionManager> <!-- 配置连接数据库的信息:用的是数据源【连接池】--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!-- 注册StudentDao接口映射文件位置 --> <mappers>    <!--配置方式一:指定全限定类名:每一个持久层的类都要配置--> <!--<mapper class="com.hpc.mapper.StudentMapper"></mapper>-->    <!--配置方式二:如果用这种方式进行配置,那么持久层的包名和这个配置文件的包名必须保持一致-->   <package name="com.hpc.mapper"/> </mappers> </configuration>

 

 

  (4)编写实体类:(com.hpc.pojo.Student)

这儿介绍一下lombok的使用,使用lombok必须导入lombok的依赖,它是一个Java库,能自动插入编辑器并构建工具,简化Java开发。通过添加注解的方式,不需要 为类编写getter或equels方法,同时可以自动化日志变量。

注意:旧版本的IDEA需要安装Lombok插件

@Setter 注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成 setter方法。

@Getter 使用方法同上,区别在于生成的是getter方法。

@ToString 注解在类,添加toString方法。

@EqualsAndHashCode 注解在类,生成hashCode和equals方法。

@NoArgsConstructor 注解在类,生成无参的构造方法。

@RequiredArgsConstructor 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被 @NonNull注解的字段。

@AllArgsConstructor 注解在类,生成包含类中所有字段的构造方法。

@Data 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final 属性,则不会为该属性生成setter方法。

 

实体类:(我这儿和数据库表中的字段名保持了一致,也可以不保持一致,后面做处理即可)

package com.hpc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int sage;
private String ssex;
private String semail;
private String sphoto;
private int cid;
}

 

  (5)编写Mapper层的接口(com.hpc.mapper.StudentMapper.java)

package com.hpc.mapper;
import com.hpc.pojo.Student;
import java.util.List;
public interface StudentMapper {
List<Student> getAllStudents();
}

 

  (6)编写Mapper层的映射文件:在resources下的包:com.hpc.mapper.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">
<!--namespace的值,就是该映射文件对应的接口-->
<mapper namespace="com.hpc.mapper.StudentMapper">
<!--id必须是接口中需要实现的方法的名字,resultType查询到的数据要封装成的类型-->
  <select id="getAllStudents" resultType="com.hpc.pojo.Student">
    select * from tb_student;
  </select>
</mapper>

 

  (7)测试代码:

package com.hpc.test;

import com.hpc.mapper.StudentMapper;
import com.hpc.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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class MyBatisTest {
@Test
public void getAllStudent(){
try {
       //读取核心配置文件
InputStream in
= Resources.getResourceAsStream("mybatis-config.xml");
       //创建构建者
SqlSessionFactoryBuilder builder
= new SqlSessionFactoryBuilder();
       //利用工厂模式进行调用,解析读取到的数据:
SqlSessionFactory factory
= builder.build(in);
       //找方法:
SqlSession sqlSession
= factory.openSession();
StudentMapper studentMapper
= sqlSession.getMapper(StudentMapper.class);
List
<Student> list = studentMapper.getAllStudent();
list.forEach(System.out::println);
}
catch (IOException e) {
e.printStackTrace();
}
}

 

查询到的数据截图:

  (8)每一次创建SqlSession都有很多的代码,可以将这部分代码进行封装,写成一个工具类,然后在需要使用的地方进行调用即可,这儿进行简单的测试,那么就在测试中调用。(com.hpc.util.SqlSeesionUtil.java)

package com.hpc.util;

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;

public class SqlSessionUtil {
private static SqlSession sqlSession;
//防止资源的浪费,而且只需要加载一次即可全局使用,所以将这部分放在静态代码块中,随着类的创建只加载一次。
static {
try {
InputStream in
= Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder
= new SqlSessionFactoryBuilder();
SqlSessionFactory factory
= builder.build(in);

        //设置为自动提交,增删改在数据库中才会有变化;
       sqlSession = factory.openSession(true);
   } catch (IOException e) { e.printStackTrace(); } } //返回一个SqlSession对象; 

  public static SqlSession getSqlSession(){
    
return sqlSession;
  }

  public static void closeSqlSession(SqlSession sqlSession){
    sqlSession.close();
  }
}

 

  •  4.使用mybatis实现CRUD(基于数据库db_school的db_student)

  (1)StudentMapper.java;(里面的方法)

  //表中的列名和实体类中的属性名不一致:解决方法1
    List<Student> getAllStudent1();
</span><span style="color: #008000;">//</span><span style="color: #008000;">表中的列名和实体类中的属性名不一致:解决方法2</span>
List&lt;Student&gt;<span style="color: #000000;"> getAllStudent2();

  //添加学生信息
int addStudent(Student student);
//通过学生学号删除学生信息
int delStudent(int sid);
//通过学号修改学生信息
int updateStudent(Student student);
//多条件查询,不同的对象,可以封装在一个新的实体类中
List<Student> getStudentBySths(QueryVo queryVo);
//多条件查询,可以传入Map集合
List<Student> getStudentBySth(Map<String, Object> map);
//模糊查询:1
List<Student> getStudentBySname(String Sname);
//模糊查询:2
List<Student> getStudentBySname2(String Sname);
//模糊查询:3
List<Student> getStudentBySname3(String Sname);

 

 

  (2)StudentMapper.xml的文件:

<!--表中的列名和实体类中的属性名不一致:解决方法1:为表的字段名取别名;id是mapper层接口中的方法-->
    <select id="getAllStudent1" resultType="com.hpc.pojo.Student">
        select sid stuId,sname,ssex,sage,semail,sphoto from tb_student;
    </select>

<!--表中的列名和实体类中的属性名不一致:解决方法2,返回的是一个Map集合-->
<select id="getAllStudent2" resultMap="getAllStudents">
select * from tb_student;
</select>
<!--这儿的id可以随意取名字,上面的resultMap来引用-->
<resultMap id="getAllStudents" type="com.hpc.pojo.Student">
<id column="sid" property="stuId"></id>
<result column="sname" property="stuName"></result>
<result column="ssex" property="stuSex"></result>
<result column="sage" property="stuAge"></result>
<result column="semail" property="stuEmail"></result>
<result column="sphoto" property="stuPhoto"></result>
</resultMap>

<!--插入学生对象,parameterType是传入的参数类型,这儿传入了一个Student类型的对象,插入的默认返回值是int类型,这儿没有返回的数据类型,下面的values里面的值是pojo中Student里面的属性名-->
<insert id="addStudent" parameterType="com.hpc.pojo.Student">
insert into tb_student(sname, sage, ssex, semail, sphoto)
values (#{sname}, #{sage}, #{ssex}, #{semail}, #{sphoto});
</insert>

<!--删除学生,传入一个参数为int类型的sid-->
<delete id="delStudent" parameterType="int">
delete from tb_student where sid = #{sid};
</delete>

<!--通过学生的id来修改学生的信息-->
<update id="updateStudent" parameterType="com.hpc.pojo.Student">
update tb_student set sname=#{sname},sage=#{sage},ssex=#{ssex},semail=#{semail},sphoto=#{sphoto} where sid = #{sid};
</update>

<!--多条件查询:-->
<!--传入的参数:封装的是一个类中QueryVo,这个类的属性包括Student student;String sname sname-->
<select id="getStudentBySths" parameterType="com.hpc.pojo.QueryVo" resultType="com.hpc.pojo.Student">
select * from tb_student where sage = #{student.sage} and sname = #{sname}
</select>
<!--传入的参数:map集合(Map<String,Object>)-->
<select id="getStudentBySth" parameterType="map" resultType="com.hpc.pojo.Student">
select * from tb_student where sname = #{sname} and sage = #{sage};
</select>

<!--模糊查询:三种方式:-->
<select id="getStudentBySname" parameterType="String" resultType="com.etime.pojo.Student">
select * from tb_student where sname like #{sname};
</select>
<!-- 下面这种模糊查询可能会导致sql注入问题:-->
<select id="getStudentBySname2" parameterType="String" resultType="com.etime.pojo.Student">
select * from tb_student where sname like '%${value}%';
</select>

<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">select </span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="getStudentBySname3"</span><span style="color: #ff0000;"> parameterType</span><span style="color: #0000ff;">="String"</span><span style="color: #ff0000;"> resultType</span><span style="color: #0000ff;">="com.etime.pojo.Student"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">
    select * from tb_student where sname like concat('%',#{sname},'%');
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">select</span><span style="color: #0000ff;">&gt;</span></pre>

 

  (3)测试 MyTest.java(这儿测试了增加的方法,也可以直接调用Mapper层中的其他方法)

    SqlSession sqlSession = SqlSessionUtil.getSqlSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    @Test
    public void test0(){
        //调用studentMapper中方法:sid是自增长的,所以可以填写默认值
        int rows = studentMapper.addStudent(new Student(0, "xh", 18, "男", "xh@1234.com", ""));
        System.out.println(rows);
        //调用SqlSessionUtil工具类中的方法,关闭资源;
        SqlSessionUtil.closeSqlSession(sqlSession);
    }

 

  (4)查看数据库结果

 

 

  (5)在StudentMapper.xml的文件中我们发现,不管是传入的参数parameterType,还是返回的参数类型resultType,如果涉及到当前项目中的某一类,都要写全限定类名,就比较麻烦,如果只写实体类中的类名,就会显得方便一些,那么我们就会在配置文件mybatis-config.xml中<environments>标签的前面那个位置配置<typeAliases>标签中相关的属性:

<typeAliases>
        <!--方式一:-->
        <!--<typeAlias type="com.hpc.mapper.StudentMapper" alias="stu"></typeAlias>-->
        <!--方式二:配置实体类中的包名,推荐使用第二种方式-->
        <package name="com.hpc.pojo"/>
 </typeAliases>

 

 (6)补充说明:parameterType 配置参数的注意事项:

  基本类型和String可以直接写类型名称也可以使用包名.类名的方式,例如:java.lang.String。 实体类类型,目前我们只能使用全限定类名。 究其原因,是mybaits在加载时已经把常用的数据类型注册了别名,从而我们在使用时可以不写包名, 而我们的是实体类并没有注册别名,所以必须写全限定类名。

  • 5.mybatis返回主键值。

  对于自增主键在某些业务中保存一个对象后,需要使用到这个主键完成后续的业务逻辑,就需要获 取该主键值。那么该如何获取这个值呢,接下来将实现该功能:

  (1)介绍insert标签中的属性值:

useGeneratedKeys: 表示开启获取自增主键值。
keyProperty:表示从表中取到主键值后赋给Student类中的哪个属性。
keyColumn: 指定取数据库中哪一列的值(通常指主键列)。
resultType: 表示对象的属性类型
order:表示完后sql语句之前还是之后把主键值赋给实体类对应属性。

  (2)在mapper接口层中有一个方法:

int addStudent(Student student);

  (3)在StudentMapper.xml中的insert标签:

<!--方式一:-->
<!--<insert id="addStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="sid">
        insert into tb_student(sname,sage,ssex,semail,sphoto) values(#{sname},#{sage},#{ssex},#{semail},#{sphoto});
</insert>-->
<!--方式二:-->
<insert id="addStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="sid">
        <selectKey resultType="int" keyProperty="sid" order="AFTER" keyColumn="sid">
            select last_insert_id();
        </selectKey>
        insert into tb_student(sname,sage,ssex,semail,sphoto) values(#{sname},#{sage},#{ssex},#{semail},#{sphoto});
</insert>

 

 

 

   (4)测试:MyBatis.java中的测试代码:

@Test
    public void test2(){
        Student student = new Student(0, "项羽", 20, "男", "xiangyu@qq.com", "");
        System.out.println(student);
        int i = studentMapper.addStudent(student);
        System.out.println(i);
        System.out.println(student);
        SqlSessionUtil.closeSqlSession(sqlsession);
    }

 

  输出的结果:(得到了sid为45;)

   数据库中的数据:

  • 6.SQL片段

  在上述我们发现,在插入的时候写的insert into tb_student(sname,sage,ssex,semail,sphoto),中的字段每次都要写,就会很麻烦,所以我们可以把重复的SQL语句进行拼接,作为共用的片段,然后引用就会很方便,这就是所谓的SQL片段,提高代码的可重用性。

  (1)在StudentMapper.xml文件中,在<mapper>标签后面定义一个<sql>片段,抽取出常用的属性;

<sql id="KeyColumn">
        sname,sage,ssex,semail,sphoto
</sql>

 

  (2)在insert标签中就可以引用,引用的方式如下:(代码只有一两个可能看不出来什么方便之处,但是类似的代码变多之后,这种方式就会显得很方便)

<!--返回主键的值:方法一:-->
   <!-- <insert id="addStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="sid">
        insert into tb_student(<include refid="KeyColumn"></include>) values(#{sname},#{sage},#{ssex},#{semail},#{sphoto});
    </insert>-->
<!--方法二:keyColumn:数据库中表中的某一列;keyProperty:对象Student中的属性-->
    <insert id="addStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="sid">
        <selectKey resultType="int" keyProperty="sid" order="AFTER" keyColumn="sid">
            select last_insert_id();
        </selectKey>
        insert into tb_student(<include refid="KeyColumn"></include>) values(#{sname},#{sage},#{ssex},#{semail},#{sphoto});
    </insert>
  • 7.mybatis动态SQL

  理解:SQL 是动态拼接成的,根据传入的变量值进行逻辑操作,并动态拼接,方便实现多条件 下的数据库操作。 在业务逻辑复杂,即简单 SQL 无法完成时,需要拼接时就要使用动态 SQL。

  (1)where标签:where标签用于代替sql中的where关键字,可以根据条件判断是否附加where关键字。如果where 标签中有条件成立就会附加where关键字,如果没有成立的条件就不会附加where关键字. 可以去掉离他 最近一个无关的and 或or关键字.where标签的书写格式为添写附加条件。

  where标签的使用

  1)StudentMapper接口中,定义个方法,根据条件查询;

List getStudentBySth(Student student);

 

   2)StudentMapper.xml文件中的SQL语句:

<select id="getStudentBySth" parameterType="Student" resultType="Student">
  select * from tb_student
  <where>
    sname=#{sname} and sage=#{sage};
  </where>
</select>

 

  3)测试:测试就是调用相应的方法,然后传入相应的参数即可;

  (2)if标签。if标签表示逻辑条件判断,如果条件成立就附加之间的sql语句,如果条件不成立就不附加之间的sql语 句。

  1)StudentMapper接口中定义方法,根据条件来进行查询(相当于多条件进行查询)

List<Student> getStudentBySth(Student student);

 

  2)StudentMapper.xml文件中的SQL语句:

<select id="getStudentBySth" parameterType="Student" resultType="Student">
  select * from tb_student
  <where>
    <if test="sname != null">
      and sname like #{sname}
    </if>
    <if test="sage != 0">
      and sage=#{sage};
    </if>
  </where>
</select>

 

  (3)set标签。set标签用于更新语句中,代替set关键字,可以有效对指定字段进行更新,提升sql的执行效率,当 set标签中有条件成立时就会附加set标签,set标签会去除无关的逗号。

   1)StudentMapper接口中定义一个修改学生信息的方法:

int updateStudent(Student student);

  2)StudentMapper.xml文件中的SQL语句。下面更改哪个属性就会变哪个属性的值,这种方式比较灵活;

<update id="updateStudent" parameterType="Student">
  update tb_student
  <set>
    <if test="sname != null">
      sname=#{sname},
    </if>
    <if test="sage != 0">
      sage=#{sage},
    </if>
    <if test="ssex != null">
      ssex=#{ssex},
    </if>
    <if test="semail != null">
      semail=#{semail},
    </if>
    <if test="sphoto != null">
      sphoto=#{sphoto},
    </if>
  </set>
  where sid=#{sid};
</update>

 

  (4)trim标签。trim标签为万能标签,可用于替换set或where等。prefix表示要附加的前缀关键字,suffix表示要附 加的后缀关键字,prefixOverrides表示要忽略前置字符,suffixOverrides表示要忽略后置字符。

  1)StudentMapper.xml文件中的SQL语句.替换上面的where标签:prefixOverrides忽略这儿的第一个and。

<select id="getStudentBySth" parameterType="Student" resultType="Student">
  select * from tb_student
  <trim prefix="where" prefixOverrides="and">
    <if test="sname != null">
      and sname like #{sname}
    </if>
    <if test="sage != 0">
      and sage=#{sage};
    </if>
  </trim>
</select>

 

   2)StudentMapper.xml文件中的SQL语句.替换上面的set标签:suffixOverrides忽略这儿的最后一个逗号。

<update id="updateStudent" parameterType="Student">
  update tb_student
  <trim prefix="set" suffixOverrides=",">
    <if test="sname != null">
      sname=#{sname},
    </if>
    <if test="sage != 0">
      sage=#{sage},
    </if>
    <if test="ssex != null">
      ssex=#{ssex},
    </if>
    <if test="semail != null">
      semail=#{semail},
    </if>
    <if test="sphoto != null">
      sphoto=#{sphoto},
    </if>
  </trim>
  where sid=#{sid};
</update>

 

  (5)choose标签。choose标签作用条件判断来拼接指定的条件,它和if不太相同,choose似类于java中的switch语句用法,要有条件成立,其它判断将得不到执行,如果所有条件都不成立则执行otherwise标签中的内容(也就是相当于在switch-case中的case的语句后面加了break的效果)。

  1)StudentMapper接口中的方法。通过某条件查询学生的信息:

List<Student> getStudentBySth1(Student student);

 

  2)StudentMapper.xml文件中的SQL语句。这儿的效果相当于是查询学生的信息,要么通过学生的名字来模糊查询,要么通过年龄,要么通过性别,要么通过电子邮件。只能选择一个条件查询,查询完就结束,不是多条件,而是从众多条件中选一个,和多级联查要区分开。

<select id="getStudentBySth1" parameterType="Student" resultType="Student" >
  select * from tb_student
  <where>
    <choose>
      <when test="sname != null">
      and sname like #{sname}
      </when>
      <when test="sage != 0">
      and sage = #{sage}
      </when>
      <when test="ssex != null">
      and ssex = #{ssex}
      </when>
      <otherwise>
      and semail = #{semail}
      </otherwise>
    </choose>
  </where>
</select>

 

  (6)foreach标签。foreach标签表示循环,对sql中有重复的部分可以使用此循环来动态拼接sql语句。可以实现批量添 加、批量删除、批量更新操作。foreach标签中有很多的属性,请参考下面的Foreach标签属性表。实现批量操作方式,需要在url后面加allowMultiQueries=true。

属性名称 含义
collection 指定你要使用的集合类型
item 集合中每个元素。
open 在起始时,需要附加字符串,只附加一次。
close 在结束时,需要附加字符,只附加一次。
separator 在每个循环结时需要附加的字符串。
index 每个循环的索引值。

 

  1)StudentMapper接口中的方法

int addBatchStudent(List<Student> students);
int delBatchStudent(List<Integer> sids);
int updateBatchStudent(List<Student> students);

 

  2)StudentMapper.xml文件中的SQL语句

<insert id="addBatchStudent" parameterType="List">
  insert into tb_student(<include refid="baseColumn"></include>) values
  <foreach collection="list" item="stu" separator=",">
    (#{stu.sname},#{stu.ssex},#{stu.sage},#{stu.semail},#{stu.sphoto})
  </foreach>
</insert>
<delete id="delBatchStudent" parameterType="List">   delete from tb_student where sid in   <foreach collection="list" open="(" close=")" item="sid" separator=",">   #{sid}   </foreach> </delete>
<update id="updateBatchStudent" parameterType="List">   <foreach collection="list" item="stu" separator=";">     update tb_student set sname=#{stu.sname},ssex=#{stu.ssex},sage=#{stu.sage},semail=#{stu.semail},sphoto=#{stu.sphoto} where sid=#{stu.sid}   </foreach> </update>

 

  •  8.在mybatis中的多表联查

   (1)一对一

  1)数据库中的设计:设计了tb_capital和tb_country两个表,结果如下:(直接导的数据库中的表结构)任务:查出所有首都以及对应的国家;

  tb_capital:(cid作为外键)

DROP TABLE IF EXISTS `tb_capital`;
CREATE TABLE `tb_capital`  (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `cid` int NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE INDEX `cid`(`cid` ASC) USING BTREE,
  CONSTRAINT `cid` FOREIGN KEY (`cid`) REFERENCES `tb_country` (`cid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

 

  tb_country:

DROP TABLE IF EXISTS `tb_country`;
CREATE TABLE `tb_country`  (
  `cid` int NOT NULL AUTO_INCREMENT,
  `cname` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`cid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

 

 

  2)实体类:(com.hpc.pojo)

  Capital.java

package com.hpc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Capital {
private int id;
private String name;
private int cid;
private Country country;
}

 

  Country.java

package com.hpc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Country {
private int cid;
private String cname;
}

 

  3)mapper层:com.hpc.mapper中,

package com.hpc.mapper;

import com.hpc.pojo.Capital;

import java.util.List;

public interface CapitalMapper {
//查询出首都以及国家的名字:
//两种方法:两表联查;查了又查
List<Capital> getCapitalAndCountry();
List
<Capital> getCapitalAndCountry1();
}

  4)映射文件中:CapitalMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hpc.mapper.CapitalMapper">
<!--方式一:多表联查:(多表联查,不涉及到再去查询Country中的数据,这样就结束了)-->
    <select id="getCapitalAndCountry" resultMap="capitalMap">
        select * from tb_capital ca,tb_country co where ca.cid = co.cid
    </select>
    <resultMap id="capitalMap" type="Capital">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <association property="country" javaType="Country">
            <id property="cid" column="cid"></id>
            <result property="cname" column="cname"></result>
        </association>
    </resultMap>
<!--方式二:查了再查:--> <select id="getCapitalAndCountry1" resultMap="capitalMap1"> select * from tb_capital; </select> <resultMap id="capitalMap1" type="Capital"> <id property="id" column="id"></id> <result property="name" column="name"></result> <!--column:代表的是要传入到指定查询中的数据;select:代表要调用的指定的查询,去找com.hpc.mapper。CountryMapper中的getCountryByCid来查询,通过这儿Column传入的参数cid去查询;--> <association property="country" javaType="Country" column="cid" select="com.hpc.mapper.CountryMapper.getCountryByCid"> </association> </resultMap> </mapper>

 

  5)查了又查的补充:接口中:CountryMapper.java

package com.hpc.mapper;

import com.hpc.pojo.Country;

import java.util.List;

public interface CountryDao {
//根据id来查询国家信息:
Country getCountryByCid(int cid);
}

 

 

  6)然后映射文件根据mapper层中的方法进行SQL语句:CountryMapper.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hpc.mapper.CountryMapper">
<span style="color: #0000ff;">&lt;</span><span style="color: #800000;">select </span><span style="color: #ff0000;">id</span><span style="color: #0000ff;">="getCountryByCid"</span><span style="color: #ff0000;"> parameterType</span><span style="color: #0000ff;">="int"</span><span style="color: #ff0000;"> resultType</span><span style="color: #0000ff;">="Country"</span><span style="color: #0000ff;">&gt;</span><span style="color: #000000;">
    select * from tb_country where cid=#{cid};
</span><span style="color: #0000ff;">&lt;/</span><span style="color: #800000;">select</span><span style="color: #0000ff;">&gt;</span>

</mapper>

 

  (2)一对多。学生和班级的关系tb_student和tb_class;

  1)表结构:

   tb_class:

DROP TABLE IF EXISTS `tb_class`;
CREATE TABLE `tb_class`  (
  `cid` int NOT NULL AUTO_INCREMENT,
  `cname` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`cid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

 

 

  tb_student:

 

DROP TABLE IF EXISTS `tb_student`;
CREATE TABLE `tb_student`  (
  `sid` int NOT NULL AUTO_INCREMENT,
  `sname` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `sage` int NULL DEFAULT NULL,
  `ssex` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `semail` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `sphoto` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `cid` int NULL DEFAULT NULL,
  PRIMARY KEY (`sid`) USING BTREE,
  INDEX `class_cid`(`cid` ASC) USING BTREE,
  CONSTRAINT `class_cid` FOREIGN KEY (`cid`) REFERENCES `tb_class` (`cid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 43 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

 

  2)StudentMapper接口中的方法:

List<Student> getAllStudentAndClass();

 

  3)StudentMapper.xml文件中:(这儿就写了一种方式,多表联查,如果要用查了又查的方式,可以根据cid来查询班级信息,但是这种方式相对于联查的方式效率要低一些)

<!--多表联查:-->
    <select id="getAllStudentAndClass" resultMap="StudentClassMap">
        select * from tb_student s,tb_class c where s.cid=c.cid;
    </select>
    <resultMap id="StudentClassMap" type="Student">
        <id property="sid" column="sid"></id>
        <result property="sname" column="sname"></result>
        <result property="sage" column="sage"></result>
        <result property="ssex" column="ssex"></result>
        <result property="semail" column="semail"></result>
        <result property="sphoto" column="sphoto"></result>
        <result property="cid" column="cid"></result>
        <association property="classes" javaType="Classes">
            <id property="cid" column="cid"></id>
            <result property="cname" column="cname"></result>
        </association>
    </resultMap>

 

  5)创建班级的实体类:在com.hpc.pojo包下创建:Classes类

package com.hpc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Classes {
private int cid;
private String cname;
}
  

  6)因为每个学生对应一个班级对象,所以学生类中如果要存储班级的信息,那么在Student实体类中加一个属性Classes classes  

package com.hpc.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int sage;
private String ssex;
private String semail;
private String sphoto;
private int cid;
private Classes classes;
}

 

  7)最后查询的结果如下

  (3)多对多。学生和课程之间的关系,课程表的结果如下:(需要的数据自行插入)

  1)数据库表:

  tb_course:

DROP TABLE IF EXISTS `tb_course`;
CREATE TABLE `tb_course`  (
  `courseid` int NOT NULL AUTO_INCREMENT,
  `coursename` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`courseid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

 

  中间表:tb_sc:

DROP TABLE IF EXISTS `tb_sc`;
CREATE TABLE `tb_sc`  (
  `scid` int NOT NULL AUTO_INCREMENT,
  `sid` int NULL DEFAULT NULL,
  `courseid` int NULL DEFAULT NULL,
  PRIMARY KEY (`scid`) USING BTREE,
  INDEX `sid`(`sid` ASC) USING BTREE,
  INDEX `courseid`(`courseid` ASC) USING BTREE,
  CONSTRAINT `tb_sc_ibfk_1` FOREIGN KEY (`sid`) REFERENCES `tb_student` (`sid`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `tb_sc_ibfk_2` FOREIGN KEY (`courseid`) REFERENCES `tb_course` (`courseid`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 30 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

 

  2)实体类:

  Student.java

package com.hpc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Student {
private int sid;
private String sname;
private int sage;
private String ssex;
private String semail;
private String sphoto;
private int cid;
private List<Course> courses;
}

 

  Course.java

package com.hpc.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Course {
private int courseid;
private String coursename;
}

 

   3)StudentMapper接口中的方法:

List<Student> getStudentAndCourse();

  4)StudentMapper.xml中的配置:

<select id="getStudentAndCourse" resultMap="studentCourseMap">
  SELECT * FROM tb_student s,tb_sc sc,tb_course c
  WHERE s.sid = sc.sid AND sc.courseid = c.courseid;
</select>
<resultMap id="studentCourseMap" type="Student">
  <id property="sid" column="sid"></id>
  <result property="sname" column="sname"></result>
  <result property="sage" column="sage"></result>
  <result property="ssex" column="ssex"></result>
  <result property="semail" column="semail"></result>
  <result property="sphoto" column="sphoto"></result>
  <result property="cid" column="cid"></result>
  <collection property="courses" ofType="Course">
    <id property="courseid" column="courseid"></id>
    <result property="coursename" column="coursename"></result>
  </collection>
</resultMap>

 

   5)测试:MyBatis.java

  SqlSession sqlSession = SqlSessionUtil.getSqlSession();
   StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
 @Test
    public void test1(){
        List<Student> students = studentMapper.getStudentAndClass();
        students.forEach(System.out::println);
        sqlSession.close();
    }

 

  • 9.Mybatis的延迟加载

  (1)什么是延迟加载?

  延迟加载(lazy load)是(也称为懒加载)关联关系对象默认的加载方式,延迟加载机制是为了避免一些 无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。 延迟加载,可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。

  (2)为什么要使用延迟加载?

  减少访问数据库的频率,我们要访问的数据量过大时,明显用缓存不太合适,因为内存容量有限为 了减少并发量,减少系统资源的消耗。 mybatis应用延迟加载的前得条件必须是嵌套方式才可以哦。association、collection执行再查操作 时具备延迟加载功能。

  (3)全局延迟加载的配置:(mybatis-config.xml核心配置文件中进行配置)在properties标签的后面进行配置:

  • 10.Mybatis的缓存

  (1)介绍:缓存是存在于内存中的临时数据,使用缓存的目的是减少和数据库的交互次数,提高执行效率。像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能,Mybatis 中缓存分为一级缓存,二级缓存。

  (2)一级缓存。mybatis一级缓存一种是SESSION级别的,针对同一个会话SqlSession中,执行多次条件完全相同的同一个sql,那么会共享这一缓存。特点:自带,不能卸载;SqlSession级别的,使用的时候无需配置;

    1)图解:

  

    2)核心配置文件中进行配置,方便在控制台中打印并测试信息:

     <!--配置在控制台打印运行的sql语句-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>

    2)StudentMapper接口中的代码:

package com.hpc.mapper;
import com.hpc.pojo.Student;
public interface StudentMapper {
Student getStudentBySid(int sid);
}

 

    3)映射文件:

<?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.hpc.mapper.StudentMapper">
  <select id="getStudentBySid" parameterType="int" resultType="Student">
    select * from tb_student where sid = #{sid};
  </select>
</mapper>

 

    4)测试:

package com.hpc.test;
import com.hpc.mapper.StudentMapper;
import com.hpc.pojo.Student;
import com.hpc.util.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class MybatisTest {
@Test
public void t01() {
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Student stu1 = studentMapper.getStudentBySid(1001);
System.out.println(stu1);
System.out.println("-------------------------------------------------------------");
Student stu2 = studentMapper.getStudentBySid(1001);
System.out.println(stu2);
SqlSessionUtil.closeSqlSession(sqlSession);
}
}

 

 

    5)控制台结果:

    6)分析:从上面的代码可以出,我们写了两次查询操作,但在访问数据时,只有一次。第一次先从一级缓存中获取,因为session是新创建的,一级缓存 中没有数据,于是就查询数据获取数据,然后把查询的数据放到一级缓存中,此时一定要注意的是,一级缓存是一个Map集合,map的key是你的 查询条件字符串,值就是查询出来的对象。等第二次查询时,先从一缓存中获取,因为上一次查询后已经放到一级缓存中了,所以从一级缓存中 获取到了,就不用访问数据库了,减少和数据次的一次交互,提高了执行效率。

    7)可以将一级缓存进行清空,那么查询相同的数据每次都要去SQL中重新查询。清空的方式有三种:a.clearCache();b.执行数据库操作(增删改);c.手动提交事务:commit(); 

    clearCache来测试:

  

     结果:

  

 

  (3)二级缓存。:默认是不开启的, 使用需要配置。二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。

    1)图解:

  

    2)在核心配置文件中(mybatis-config.xml)开启二级缓存

     <!--开启二级缓存:-->
        <setting name="cacheEnabled" value="true"/>

 

     3)在映射文件中进行配置:StudentMapper.xml文件中:

  

    4)

 public void t01() {
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        Student stu1 = studentMapper.getStudentBySid(1001);
        System.out.println(stu1);
        System.out.println("-------------------------------------------------------------");
        Student stu2 = studentMapper.getStudentBySid(1001);
        System.out.println(stu2);
        SqlSessionUtil.closeSqlSession(sqlSession);
        sqlSession.commit();
   }

 

    5)测试结果:

  

  •  11.Mybatis分页插件:

  (1)介绍:分页是一种将所有数据分段展示给用户的技术.用户每次看到的不是全部数据,而是其中的一部分,如果在其中没有找到自己想要的内容,用户可以通过制定页码或是翻页的方式 转换可见内容,直到找到自己想要的内容为止。

    分页的的好处:

    a.提高性能,一次查20个,比一次查20000个性能肯定更好;另外如果数据量很大,一次性将内容都查询出来,查询出来的结果是放在内存存里面的,会增加cpu的开销造成 内存的浪费,效率极低。

    b.展现层面的考虑:如果一次展现太多的数据,不管是排版,还是美观上都不好。

  (2)使用:在pom.xml文件中引入关于分页插件的依赖:

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

  (3)在mybatis-config.xml文件中配置分页。(在environments标签之前)

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

 

   (4)在接口StudentMapper中定义方法:

List<Student> getStudentByPage();

  (5)在映射文件中:StudentMapper.xml

<select id="getStudentByPage" resultType="Student">
  select * from tb_student
</select>

 

   (6)测试:

@Test
public void t01() {
//配置分页信息,必须放在最前面
PageHelper.startPage(2,5);
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
//加载要执行的SQL语句的主体部分(不会执行这个SQL语句)
List<Student> list = studentMapper.getStudentByPage();
//根据分页配置信息,进行SQL语句的完善并执行,将相关结果放入studentPageInfo中
PageInfo<Student> studentPageInfo = new PageInfo<>(list);
System.out.println("-------------------------------------------------------------------");
//当前页数据
List<Student> studentList = studentPageInfo.getList();
studentList.forEach(System.out::println);
System.out.println("上一页页码:"+studentPageInfo.getPrePage());
System.out.println("下一页页码:"+studentPageInfo.getNextPage());
System.out.println("总页数:"+studentPageInfo.getPages());
SqlSessionUtil.closeSqlSession(sqlSession);
}

 

posted @ 2023-10-16 21:10  emphemeral  阅读(38)  评论(0)    收藏  举报