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<Student><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;"><</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;">></span><span style="color: #000000;"> select * from tb_student where sname like concat('%',#{sname},'%'); </span><span style="color: #0000ff;"></</span><span style="color: #800000;">select</span><span style="color: #0000ff;">></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;"><</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;">></span><span style="color: #000000;"> select * from tb_country where cid=#{cid}; </span><span style="color: #0000ff;"></</span><span style="color: #800000;">select</span><span style="color: #0000ff;">></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); }

浙公网安备 33010602011771号