什么 mybatis: 是一个持久层框架, 原名是ibatis, 2013改名为 MyBatis. MyBatis可以操作数据库,对数据执行增删改查。 看做是高级的jdbc。 解决jdbc的缺点。
mybatis能做什么?
1) 注册驱动 。
2) 创建jdbc中使用的Connection, Statement,ResultSet
3) 执行sql语句, 得到ResultSet
4) 处理ResultSet, 把记录集中的数据转为java对象, 同时还能把java对象放入到List集合。
5)关闭资源
6)实现sql语句和java代码的解耦合。
mybatis的文档: https://mybatis.org/mybatis-3/zh/index.html


MyBatis入门

基本操作

实现步骤:

  1. 0.创建student表(idnameemailage
  2. 1.新建maven项目
  3. 2.修改pom.xml
  4. 1)加入依赖 mybatis依赖, mysql驱动, junit
  5. 2)在<build>加入资源插件
  6. 3.创建实体类Student。定义属性, 属性名和列名保持一致
  7. 4.创建Dao接口, 定义操作数据库的方法。
  8. 5.创建xml文件(mapper文件), sql语句。
  9. mybatis框架推荐是把sql语句和java代码分开
  10. mapper文件:定义和dao接口在同一目录, 一个表一个mapper文件。
  11. 6.创建mybatis的主配置文件(xml文件):有一个, 放在resources目录下
  12. 1)定义创建连接实例的数据源(DataSource)对象
  13. 2) 指定其他mapper文件的位置
  14. 7.创建测试的内容。
  15. 使用main方法,测试mybatis访问数据库
  16. 也可以使用junit 访问数据库

目录结构如下
图片.png
pom.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4. <modelVersion>4.0.0</modelVersion>
  5. <!-- 当前项目的坐标-->
  6. <groupId>com.itheima</groupId>
  7. <artifactId>mybatis_demo</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <properties>
  10. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  11. <maven.compiler.source>1.8</maven.compiler.source>
  12. <maven.compiler.target>1.8</maven.compiler.target>
  13. </properties>
  14. <dependencies>
  15. <dependency>
  16. <groupId>junit</groupId>
  17. <artifactId>junit</artifactId>
  18. <version>4.11</version>
  19. <scope>test</scope>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.mybatis</groupId>
  23. <artifactId>mybatis</artifactId>
  24. <version>3.5.1</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>mysql</groupId>
  28. <artifactId>mysql-connector-java</artifactId>
  29. <version>5.1.9</version>
  30. </dependency>
  31. </dependencies>
  32. <build>
  33. <!-- 资源插件:处理src/main/java目录中的xml-->
  34. <resources>
  35. <resource>
  36. <directory>src/main/java</directory>
  37. <includes><!--包括目录下的.properties,.xml文件都会扫描到-->
  38. <include>**/*.properties</include>
  39. <include>**/*.xml</include>
  40. </includes>
  41. <filtering>false</filtering>
  42. </resource>
  43. </resources>
  44. </build>
  45. </project>

mybatis.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <environments default="development">
  7. <environment id="development">
  8. <transactionManager type="JDBC"/>
  9. <!--配置数据源-->
  10. <dataSource type="POOLED">
  11. <property name="driver" value="com.mysql.jdbc.Driver"/>
  12. <property name="url" value="jdbc:mysql:///test"/>
  13. <property name="username" value="root"/>
  14. <property name="password" value="liuhuande123"/>
  15. </dataSource>
  16. </environment>
  17. </environments>
  18. <!--指定其他mapper文件的位置:其他mapper文件目的是找到其他文件的sql语句-->
  19. <mappers>
  20. <!--
  21. 使用mapperresource属性指定mapper文件的路径
  22. 这个路径是从target/classes路径开启的
  23. 使用注意:
  24. resource=“mapper文件的路径,使用/分割路径”
  25. 一个mapper resource指定一个mapper文件
  26. -->
  27. <mapper resource="com/itheima/dao/StudentDao.xml"/>
  28. </mappers>
  29. </configuration>

StudentDao.xml

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.itheima.dao.StudentDao">
  6. <!--查询一个学生
  7. id:要执行的sql语句的唯一标识,推荐和方法名一样
  8. resultType:告诉mybatis,执行sql语句,把数据赋值给哪个类型的java对象
  9. 使用java对象的全限定名称
  10. -->
  11. <select id="selectStudentById" resultType="com.itheima.domin.Student">
  12. select * from tbl_employee where id=1
  13. </select>
  14. </mapper>
  15. <!--
  16. 1.约束文件
  17. "http://mybatis.org/dtd/mybatis-3-mapper.dtd"
  18. 约束文件的作用:定义和限制当前文件中可以使用的标签和属性,以及标签出现的顺序
  19. 2.mapper是根标签
  20. namespace:命名空间,必须有值,不能为空。唯一值
  21. 推荐使用Dao接口的全限定名称
  22. 作用:参与识别sql语句的作用
  23. 3.mapper里面可以写<insert>,<update>,<delete>,<select>
  24. -->

Student.java

  1. package com.itheima.domin;
  2. import com.sun.org.apache.xpath.internal.objects.XString;
  3. public class Student {
  4. private Integer id;
  5. private String last_name;
  6. private String gender;
  7. private String email;
  8. @Override
  9. public String toString() {
  10. return "Student{" +
  11. "id=" + id +
  12. ", last_name='" + last_name + '\'' +
  13. ", gender='" + gender + '\'' +
  14. ", email='" + email + '\'' +
  15. '}';
  16. }
  17. public Integer getId() {
  18. return id;
  19. }
  20. public void setId(Integer id) {
  21. this.id = id;
  22. }
  23. public String getLast_name() {
  24. return last_name;
  25. }
  26. public void setLast_name(String last_name) {
  27. this.last_name = last_name;
  28. }
  29. public String getGender() {
  30. return gender;
  31. }
  32. public void setGender(String gender) {
  33. this.gender = gender;
  34. }
  35. public String getEmail() {
  36. return email;
  37. }
  38. public void setEmail(String email) {
  39. this.email = email;
  40. }
  41. }

StudentDao.java

  1. package com.itheima.dao;
  2. import com.itheima.domin.Student;
  3. public interface StudentDao {
  4. //查询一个学生
  5. Student selectStudentById(Integer id);
  6. }

使用占位符

mybatis中使用#{}作占位符

  1. <select id="selectStudentById" resultType="com.itheima.domin.Student">
  2. select * from tbl_employee where id=#{id}
  3. </select>

查询的时候可以通过参数参数进行查询

插入数据

  • mybatis默认执行的sql语句是手工提交事务模式,在做insert,update,delete后需提交事务 ```java SqlSession sqlSession = sqlSessionFactory.openSession(true);//设置自动提交

sqlSession.commit();//或者手动提交

  1. - 在插入的时候如果传入的参数是一个对象,则在sql语句中占位符的名字可以写属性值,将会默认执行get方法
  2. ```java
  3. <insert id="insertStudent">
  4. insert into tbl_employee values(#{id},#{last_name},#{gender},#{email})
  5. </insert>
  1. @Test
  2. public void testInsertStudent() throws IOException {
  3. //调用mybatis某个对象的方法,执行mapper文件中的sql语句
  4. //mybatis核心类:SqlSessionFactory
  5. //1.定义mybatis主配置文件的位置,从类路径开始的相对路径
  6. String resource = "mybatis.xml";
  7. //2.读取主配置文件。使用mybatis框架中的Resources类
  8. InputStream inputStream = Resources.getResourceAsStream(resource);
  9. //3.创建SqlSessionFactory对象。使用SqlSessionFactoryBuilder类
  10. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  11. //4.获取SqlSession对象
  12. SqlSession sqlSession = sqlSessionFactory.openSession();
  13. //5.指定要执行的sql语句的id
  14. //sql的id=namespace+"."+标签的id属性值
  15. String sqlId="com.itheima.dao.StudentDao"+"."+"insertStudent";
  16. //6.通过SqlSession的方法执行sql语句
  17. Student student1 = new Student(3, "amy", "0", "amy.com");
  18. int len = sqlSession.update(sqlId, student1);
  19. System.out.println(len);
  20. sqlSession.commit();
  21. //7.关闭SqlSession对象
  22. sqlSession.close();
  23. }

创建模板文件

在setting中设置
图片.png
图片.png

MyBatis的一些重要对象

1) Resources : mybatis框架中的对象, 一个作用读取 主配置信息。

  1. InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");

2)SqlSessionFactoryBuilder:负责创建SqlSessionFactory对象

  1. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

3)SqlSessionFactory: 重要对象
SqlSessionFactory是重量级对象:创建此对象需要使用更多的资源和时间。 在项目中有一个就可以了。
SqlSessionFactory接口:作用是SqlSession的工厂, 就是创建SqlSession对象。
DefaultSqlSessionFactory实现类

  1. public class DefaultSqlSessionFactory implements SqlSessionFactory { }

SqlSessionFactory接口中的方法
openSession(): 获取一个默认的SqlSession对象, 默认是需要手工提交事务的。
openSession(boolean): boolean参数表示是否自动提交事务。
true: 创建一个自动提交事务的SqlSession
false: 等同于没有参数的openSession
4)SqlSession对象
SqlSession对象是通过SqlSessionFactory获取的。 SqlSession本身是接口
DefaultSqlSession: 实现类

  1. public class DefaultSqlSession implements SqlSession { }

SqlSession作用是提供了大量的执行sql语句的方法:

  1. selectOne:执行sql语句,最多得到一行记录,多余1行是错误。
  2. selectList:执行sql语句,返回多行数据
  3. selectMap:执行sql语句的,得到一个Map结果
  4. insert:执行insert语句
  5. update:执行update语句
  6. delete:执行delete语句
  7. commit:提交事务
  8. rollback:回顾事务

注意SqlSession对象不是线程安全的, 使用的步骤:
①:在方法的内部,执行sql语句之前,先获取SqlSession对象
②:调用SqlSession的方法,执行sql语句
③:关闭SqlSession对象,执行SqlSession.close()

创建MyBatis工具类

  1. package com.itheima.utils;
  2. import org.apache.ibatis.io.Resources;
  3. import org.apache.ibatis.session.SqlSession;
  4. import org.apache.ibatis.session.SqlSessionFactory;
  5. import org.apache.ibatis.session.SqlSessionFactoryBuilder;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. public class MyBatisUtil {
  9. private static SqlSessionFactory factory=null;
  10. static {
  11. String config="mybatis.xml";
  12. InputStream inputStream = null;
  13. try {
  14. inputStream = Resources.getResourceAsStream(config);
  15. factory= new SqlSessionFactoryBuilder().build(inputStream);
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. //创建方法,获取SqlSession对象
  21. public static SqlSession getSqlSession(){
  22. SqlSession session=null;
  23. if (factory!=null){
  24. session = factory.openSession();
  25. }
  26. return session;
  27. }
  28. }

MyBatis的Dao代理

dao代理

mybatis提供代理:

mybatis创建Dao接口的实现类对象, 完成对sql语句的执行。 mybatis创建一个对象代替你的 dao实现类功能。

使用mybatis代理要求

1)mapper文件中的namespace 一定dao接口的全限定名称
2)mapper文件中 标签的id是dao接口方法名称

mybatis代理实现方式

使用SqlSession对象的方法 getMapper(dao.class)
例如: 现在有 StudentDao接口。

  1. SqlSession session = MyBatisUtils.getSqlSession();
  2. StudentDao dao = session.getMapper(StudentDao.class);
  3. Student student = dao.selectById(1001);
  4. //上面代码中
  5. StudentDao dao = session.getMapper(StudentDao.class);
  6. 等同于
  7. StudentDao dao = new StudentDaoImpl();

理解参数

理解参数是: 通过java程序把数据传入到mapper文件中的sql语句。 参数主要是指dao接口方法的形参

parameterType

parameterType:表示参数的类型, 指定dao方法的形参数据类型。 这个形参的数据类型是给mybatis使用。 mybatis在给sql语句的参数赋值时使用。 PreparedStatement.setXXX( 位置, 值)

  1. 第一个用法: java类型的全限定类型名称 parameterType="java.lang.Integer"
  2. 第二个用法: mybatis定义的java类型的别名 parameterType="int"
  3. parameterType:mybatis通过反射机制可以获取 dao接口方法参数的类型, 可以不写
  4. <select id="selectById" parameterType="integer" resultType="com.bjpowernode.domain.Student">
  5. select id,name,email,age from student where id=#{studentId}
  6. </select>

传递单个参数

//dao接口的方法形参是一个简单类型的
//简单类型: java基本数据类型和String
Student selectByEmail(String email);
<!--
   dao接口是一个简单类型的参数
   mapper文件,获取这个参数值,使用#{任意字符}
-->
<select id="selectByEmail" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where email=#{studentEmail}
</select>

使用注解传递参数

@Param: 命名参数, 在方法的形参前面使用的, 定义参数名。 这个名称可以用在mapper文件中。如果有多个参数的话必须使用该注解
dao接口,方法的定义

/*
  多个简单类型的参数
  使用@Param命名参数, 注解是mybatis提供的
  位置:在形参定义的前面
  属性:value 自定义的参数名称
 */
List<Student> selectByNameOrAge(@Param("myname") String name,
                                @Param("myage") Integer age);

mapper文件

<!--
   多个简单类型的参数.
   当使用了@Param命名后,例如@Param("myname").
   在mapper中,使用#{命名的参数}, 例如 #{myname}
-->
<select id="selectByNameOrAge" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{myname} or age=#{myage}
</select>

按对象传递参数

方法的形参是一个java对象。这个java对象表示多个参数。使用对象的属性值作为参数使用
java对象

public class Student {
    private Integer id;
    private String name;
    private String email;
    private Integer age;
    //set|get方法
}

public class QueryParam {
    private Object p1;
    private Object p2;
    //set|get方法
}

dao接口中的方法定义

/*
 * 一个java对象作为参数( 对象由属性, 每个属性有set,get方法)
 */
List<Student> selectByObject(Student student);
List<Student> selectByQueryParam(QueryParam param);

mapper文件

<!--
   一个java对象作为方法的参数,使用对象的属性作为参数值使用
   简单的语法: #{属性名} , mybatis调用此属性的getXXX()方法获取属性值
-->
<select id="selectByObject" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{name} or age=#{age}
</select>

<select id="selectByQueryParam" resultType="com.bjpowernode.domain.Student">
     select id,name,email,age from student where name=#{p1} or age=#{p2}
</select>

<!--负责的语法格式: #{属性名,javaType=java类型的全限定名称,jdbcType=mybatis中定义列的数据类型}-->
<select id="selectByObject" resultType="com.bjpowernode.domain.Student">
        select id,name,email,age from student where
        name=#{name,javaType=java.lang.String,jdbcType=VARCHAR}
        or
        age=#{age,javaType=java.lang.Integer,jdbcType=INTEGER}
</select>

按位置传递参数

参数位置: dao接口中方法的形参列表,从左往右,参数位置是 0 ,1,2……
语法格式:#{arg0} ,#{arg1}
dao接口的方法

/*
   使用位置,获取参数
 */
List<Student> selectByPosition(String name,Integer age);
<!--
   mybatis版本是 3.5.1
   使用位置获取参数值, dao接口方法是多个简单类型的参数
   语法: #{arg0}, #{arg1}....
-->
<select id="selectByPosition" resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where name=#{arg0} or age=#{arg1}
</select>

使用Map传递参数

map作为dao接口的参数, 使用 key 获取参数值,mapper文件中,语法格式 #{key}

/*
   使用Map作为参数
 */
List<Student> selectStudentByMap(Map<String,Object> map);

mapper文件

<!--
   使用Map传递参数,
   在mapper文件中,获取map的值,是通过key获取的,语法:#{key}
-->
<select id="selectStudentByMap" resultType="com.bjpowernode.domain.Student">
     select id,name,email,age from student where name=#{myname} or age=#{myage}
</select>

测试,调用方法的位置

@Test
public void testSelectByMap(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao  = sqlSession.getMapper(StudentDao.class);

    //使用map传递参数
    Map<String,Object> data = new HashMap<>();
    data.put("myname", "李思思");
    data.put("myage", 20);
    List<Student> students = dao.selectStudentByMap(data);

    students.forEach( stu-> System.out.println("stu="+stu));
    sqlSession.close();

}

获取自增主键

 <!--
        mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys()
        useGeneratedKeys="true";使用自增主键获取主键值策略
        keyProperty:指定对应的主键属性,也就是mybatis获取到主键值后,将这个值封装给javaBean的哪个属性
    -->
    <insert id="insertStudent" parameterType="com.itheima.domin.Student"
            useGeneratedKeys="true" keyProperty="id">
        insert into tbl_employee values(#{id},#{last_name},#{gender},#{email})
    </insert>
//6.通过SqlSession的方法执行sql语句
 Student student1 = new Student(null, "zh", "1", "amy.com");
 int len = sqlSession.update(sqlId, student1);
 sqlSession.commit();
 System.out.println(student1.getId());

#和$的区别

# 占位符

语法: #{字符}
mybatis处理#{} 使用jdbc对象是 PrepareStatment对象

<select id="selectById"  parameterType="integer"
             resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where id=#{studentId}
</select>

mybatis去创建PrepareStatement对象,执行sql语句
String sql=" select id,name,email,age from student where id=?";
PrepareStatement pst = conn.prepareStatement(sql);
pst.setInt(1,1001);  //传递参数
ResultSet rs  = pst.executeQuery(); //执行sql语句

{}特点:
1)使用的PrepareStatement对象,执行sql语句,效率高。
2)使用的PrepareStatement对象,能避免sql语句, sql语句执行更安全。
3) #{} 常常作为 列值使用的, 位于等号的右侧, #{}位置的值和数据类型有关的。

$ 占位符

语法 : ${字符}
mybatis执行${}占位符的sql语句

<select id="selectById"  parameterType="integer"
             resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where id=${studentId}
</select>    

${} 表示字符串连接, 把sql语句的其他内容和 ${}内容使用 字符串(+) 连接的方式连在一起
String sql="select id,name,email,age from student where id=" + "1001";

mybatis创建Statement对象, 执行sql语句。
Statement stmt  = conn.createStatement(sql);
ResultSet rs  = stmt.executeQuery();

${} 的特点
1)使用Statement对象,执行sql语句,效率低
2)${}占位符的值,使用的字符串连接方式, 有sql注入的风险。 有代码安全的问题
3)${} 数据是原样使用的, 不会区分数据类型。
4)${} 常用作 表名或者列名, 在能保证数据安全的情况下使用 ${}

封装MyBatis输出结果

封装输出结果: MyBatis执行sql语句,得到ResultSet, 转为java对象。
讲两个 resultType, resultMap

resultType

resultType属性: 在执行select时使用, 作为标签的属性出现的。
resultType:表示结果类型 , mysql执行sql语句,得到java对象的类型。 它的值有两种
1) java类型的全限定名称 。 2)使用别名

把返回数据封装为自定义类

Student selectById(Integer id);

<select id="selectById"  parameterType="integer"
             resultType="com.bjpowernode.domain.Student">
    select id,name,email,age from student where id=#{studentId}
</select>

resultType:现在使用java类型的全限定名称。 表示的意思 mybatis执行sql,把ResultSet中的数据转为Student类型的对象。  mybatis会做以下操作:
1. 调用com.bjpowernode.domain.Student的无参数构造方法,创建对象。
    Student student = new Student(); //使用反射创建对象
2. 同名的列赋值给同名的属性。
    student.setId( rs.getInt("id"));
    student.setName(rs.getString("name"));
3. 得到java对象, 如果dao接口返回值是List集合, mybatis把student对象放入到List集合。


所以执行 Student mystudent = dao.selectById(1001); 得到 数据库中 id=1001这行数据, 
这行数据的列值, 付给了mystudent对象的属性。 你能得到mystudent对象。 就相当于是 id=1001这行数据。

返回数据封装为简单类型

dao方法

long countStudent();

mapper文件

<!--
  执行sql语句,得到是一个值(一行一列)
-->
<select id="countStudent" resultType="java.lang.Long">
    select count(*) from student
</select>

返回数据封装为Map集合

//查询结果返回是一个Map
Map<Object,Object> selectMap(@Param("stuid") Integer id);
<!--
   执行sql得到一个Map结构数据, mybatis执行sql,把ResultSet转为map
   sql执行结果, 列名做map的key , 列值作为value
   sql执行得到是一行记录,转为map结构是正确的。

   dao接口返回是一个map, sql语句最多能获取一行记录,多余一行是错误

-->
<select id="selectMap" resultType="java.util.HashMap">
    select id,name,email from student where id = #{stuid}
</select>

当需要返回多条map时需要加上注解@MapKey告诉Map集合主键

@MapKey("id")
Map<Object,Object> selectMap(@Param("stuid") Integer id);

resultMap

resultMap: 结果映射。 自定义列名和java对象属性的对应关系。 常用在列名和属性名不同的情况。
用法:
1.先定义 resultMap标签, 指定列名和属性名称对应关系
2.在select标签使用resultMap属性,指定上面定义的resultMap的id值

<!--使用resultMap定义列和属性的关系-->
<!--定义resultMap
    id:给resultMap的映射关系起个名称,唯一值
    type:java类型的全限定名称
-->
<resultMap id="customMap" type="com.bjpowernode.vo.CustomObject">

    <!--定义列名和属性名的对应-->
    <!--主键类型使用id标签
                columns:指定哪一列
                property:指定javaBean属性
        -->

    <id column="id" property="cid" />
    <!--非主键类型使用result标签-->
    <result column="name" property="cname" />
    <!--列名和属性名相同不用定义-->
    <result column="email" property="email" />
    <result column="age" property="age" />
</resultMap>

<!--使用resultMap属性,指定映射关系的id
    resultMap和resultType 不能同时使用, 二选一。
-->
<select id="selectById2" resultMap="customMap">
  select id,name,email,age from student where id=#{stuid}
</select>
<resultMap id="MySimEmp" type="com.itheima.domin.Student">
        <id column="id" property="id"/>
        <result column="last_name" property="last_name"/>
        <result column="gender" property="gender"/>
        <result column="email" property="email"/>
    </resultMap>
    <select id="getStuById" resultMap="MySimEmp">
        select * from tbl_employee where id =#{id}
    </select>


    <resultMap id="MyDifEmp" type="com.itheima.domin.Student">
        <id column="id" property="id"/>
        <result column="last_name" property="last_name"/>
        <result column="gender" property="gender"/>
        <result column="email" property="email"/>
        <result column="did" property="dept.id"/>
        <result column="dept_name" property="dept.department"/>
    </resultMap>
    <select id="getEmpAndDept" resultMap="MyDifEmp">
        SELECT e.id,last_name,gender,email,d.id did ,dept_name FROM tbl_employee e, tbl_dept d WHERE e.`d_id`=d.`id` AND e.id=#{id}
    </select>
<resultMap id="MyDifEmp" type="com.itheima.domin.Student">
        <id column="id" property="id"/>
        <result column="last_name" property="last_name"/>
        <result column="gender" property="gender"/>
        <result column="email" property="email"/>
        <!--
        association可以指定联合的JavaBean对象
        property="dept" 指定这个属性对象的类型(不能省略)
        -->
        <association property="dept" javaType="com.itheima.domin.Department">
            <id column="did" property="id"/>
            <result column="dept_name" property="department"/>
        </association>
</resultMap>

分步查询

1.分布查询能提高查询的效率,会在需要用到子查询的时候再调用子查询的sql语句
图片.png

延迟加载

设置配置文件
图片.png

Collection-集合类型&嵌套结果集

图片.png

Collection-分步查询&延迟加载

1.首先查询根据本门id进行查询,再将该部门id传到另一个sql语句中查询员工

图片.png

鉴别器

可以根据某一列不同的取值进行查询

<discriminator javaType="string" column="gender>
    <case value="0" resultType="">
        <association property="" select="" column="">
        </association>
    </case>

    <case value="1" resultType="">
        <association property="" select="" column="">
        </association>
    </case>
</discriminator>

like

第一种方式: 在java程序中,把like的内容组装好。 把这个内容传入到sql语句

//like第一种方式
List<Student> selectLikeOne(@Param("name") String name);

mapper

<!--like第一种方式-->
<select id="selectLikeOne" resultType="com.bjpowernode.domain.Student">
    select * from student where name like #{name}
</select>

执行like

@Test
public void testLikeOne(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao  = sqlSession.getMapper(StudentDao.class);

    String name="%李%";
    List<Student> students = dao.selectLikeOne(name);

    sqlSession.close();

    students.forEach( stu-> System.out.println(stu));
}

第二种方式: 在sql语句,组织like的内容。
sql语句like的格式: where name like “%”空格#{name}空格”%”

//like第二种方式
List<Student> selectLikeTwo(@Param("name") String name);
<!--like第二种方式-->
<select id="selectLikeTwo" resultType="com.bjpowernode.domain.Student">
    select * from student where name like "%" #{name} "%"
</select>
@Test
public void testLikeTwo(){
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    StudentDao dao  = sqlSession.getMapper(StudentDao.class);

    String name="李";
    List<Student> students = dao.selectLikeTwo(name);

    sqlSession.close();

    students.forEach( stu-> System.out.println(stu));
}

动态sql

什么是动态sql: 同一个dao的方法, 根据不同的条件可以表示不同的sql语句, 主要是where部分有变化
使用mybatis提供的标签,实现动态sql的能力, 主要讲 if ,where ,foreach, sql。
使用动态sql的时候, dao方法的形参使用java对象。

if 标签

语法:


<if test="boolean判断结果">
   sql 代码
</if>


在mapper文件中
<select id="selectStudent" resultType="com.bjpwoernode.domain.Student">
      select *from student
      <if test="条件">
          sql语句
      </if>
      <if test="条件">
          sql语句
      </if>
</select>

例子:

List<Student> selectIf(Student student);
<!--if
    test: 使用对象的属性值作为条件
-->
<select id="selectIf" resultType="com.bjpowernode.domain.Student">
    select * from student
    where id=-1
    <if test="name !=null and name!=''">
       or  name = #{name}
    </if>

    <if test="age >0">
        or age &lt; #{age}
    </if>
</select>

where 标签

使用if标签时,容易引起sql语句语法错误。 使用where标签解决if产生的语法问题。
使用时 where ,里面是一个或多个if 标签, 当有一个if标签 判断条件为true, where标签会转为 WHERE 关键字附加到sql语句的后面。 如果 if 没有一个条件为true , 忽略where和里面的if。
where标签删除 和他最近的or 或者 and。

语法:
<where>
    <if test="条件1">sql语句1</if>
    <if test="条件2">sql语句2</if>
</where>

例子:

//where
List<Student> selectWhere(Student student);
<!--where-->
<select id="selectWhere" resultType="com.bjpowernode.domain.Student">
    select * from student
    <where>
        <if test="name !=null and name!=''">
            or  name = #{name}
        </if>

        <if test="age >0">
            or age &lt; #{age}
        </if>
    </where>

</select>

trim标签

自定义字符串截取规则

<!--  
  后面多出的and或者orwhere标签不能解决
  prefix="":前缀,trim标签体中是整个字符串拼接后的结果 prefix给拼串后的整个字符串加一个前缀
  prefixOverrides前缀覆盖,去掉整个字符串前面多余的字符
  suffix 后缀,trim标签体中是整个字符串拼接后的结果 prefix给拼串后的整个字符串加一个后缀
  suffixOverrides后缀覆盖,去掉整个字符串后面多余的字符
  -->
<select id="getSQl" resultType="com.itheima.domin.Student">
    select * from tbl_employee

    <trim prefix="where" suffixOverrides="and">
        <if test="id!=null">
            id=#{id} and
        </if>
        <if test="last_name!=null and last_name!=&quot;&quot;" >
            last_name=#{last_name} and
        </if>
    </trim>
</select>

choose标签

 <select id="getSQl" resultType="com.itheima.domin.Student">
        select * from tbl_employee
        <where >
            <choose>
                <when test="id!=null">
                    id=#{id}
                </when>
                <when test="last_name!=null and last_name!=&quot;&quot;" >
                    last_name=#{last_name}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>

foreach 循环

使用foreach可以循环数组,list集合, 一般使用在in语句中。
语法:

< foreach collection="集合类型" open="开始的字符" close="结束的字符"
          item="集合中的成员" separator="集合成员之间的分隔符">
              #{item 的值}
</ foreach>

标签属性:
collection: 表示,循环的对象是 数组, 还是list集合。  如果dao接口方法的形参是 数组, 
             collection="array" ,如果dao接口形参是List, collection="list"

open:循环开始时的字符。  sql.append("(");
close:循环结束时字符。  sql.append(")");
item:集合成员, 自定义的变量。   Integer item  = idlist.get(i);// item是集合成员
separator:集合成员之间的分隔符。  sql.append(","); //集合成员之间的分隔符
#{item 的值}:获取集合成员的值。

第一种方式:

//foreach-1
List<Student> selectForeachOne(List<Integer> idlist);


<!--foreach第一种方式, 循环简单类型的List-->
<select id="selectForeachOne" resultType="com.bjpowernode.domain.Student">
      select * from student
        <if test="list !=null and list.size>0">
            where id in
            <foreach collection="list" open="(" close=")" separator="," item="myid">
                  #{myid}
            </foreach>
        </if>
</select>

@Test
public void testSelectForeachOne(){
        //1.获取SqlSession
        SqlSession session = MyBatisUtil.getSqlSession();
        //2.获取dao的代理
        StudentDao dao = session.getMapper(StudentDao.class);


        List<Integer> idlist = new ArrayList<>();
        idlist.add(1001);
        idlist.add(1002);
        idlist.add(1003);


        List<Student> students  = dao.selectForeachOne(idlist);

        students.forEach( stu-> System.out.println("stu=="+stu));
        //3.关闭SqlSession对象
        session.close();
    }

第二种方式:

//foreach-2
List<Student> selectForeachTwo(List<Student> studentList);

<!--foreach第二种方式, 循环的List<Student>-->
<select id="selectForeachTwo" resultType="com.bjpowernode.domain.Student">
        select * from student
        <if test="list != null and list.size>0">
            where id in
            <foreach collection="list" open="(" close=")" separator="," item="stu">
                  #{stu.id}
            </foreach>
        </if>
</select>

 @Test
 public void testSelectForeachTwo(){
        //1.获取SqlSession
        SqlSession session = MyBatisUtil.getSqlSession();
        //2.获取dao的代理
        StudentDao dao = session.getMapper(StudentDao.class);


        List<Student> list  = new ArrayList<>();
        Student s1 = new Student();
        s1.setId(1001);

        Student s2 = new Student();
        s2.setId(1002);

        list.add(s1);
        list.add(s2);


        List<Student> students  = dao.selectForeachTwo(list);

        students.forEach( stu-> System.out.println("stu=="+stu));
        //3.关闭SqlSession对象
        session.close();
    }

sql标签

sql标签标示 一段sql代码, 可以是表名,几个字段, where条件都可以, 可以在其他地方复用sql标签的内容。
使用方式:

1) 在mapper文件中定义 sql代码片段 <sql id="唯一字符串">  部分sql语句  </sql>
2)在其他的位置,使用include标签引用某个代码片段

例如:

<!--定义代码片段-->
<sql id="selectStudent">
    select * from student
</sql>

<sql id="studentFieldList">
    id,name,email
</sql>

<select id="selectIf" resultType="com.bjpowernode.domain.Student">

        <include refid="selectStudent" />

        where id=-1
        <if test="name !=null and name!=''">
           or  name = #{name}
        </if>

        <if test="age >0">
            or age &lt; #{age}
        </if>
    </select>

    <!--where-->
    <select id="selectWhere" resultType="com.bjpowernode.domain.Student">
        select <include refid="studentFieldList"/> from student
        <where>
            <if test="name !=null and name!=''">
                or  name = #{name}
            </if>

            <if test="age >0">
                or age &lt; #{age}
            </if>
        </where>

    </select>

两个内置参数

mybatis默认有两个内置参数:
_parameter:代表整个参数
单个参数_parameter就是这个参数
多个参数_parameter被封装成为一个map,_parameter就是代表这个map
_databaseId:如果配置了databaseIdProvider标签
_databaseId就是代表当前数据库的别名

<if test="_databaseId=='mysql'">
执行的sql语句
</if>

<if test="_databaseId=='oracle'">
执行的oracle语句
</if>

bind 标签

bind标签可以讲OFNL鸟大师的值绑定到一个变量中,方便后来引用这个变量的值

 <select id="getSQl" resultType="com.itheima.domin.Student">
        select * from tbl_employee
        <bind name="_last_name" value="'%'+last_name+'%'"/>
        <where >
            <choose>
                <when test="id!=null">
                    id=#{id}
                </when>
                <when test="last_name!=null and last_name!=&quot;&quot;" >
                    last_name like #{_last_name}
                </when>
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>

MyBatis配置文件

mybatis配置文件两大类: 1 mybatis主配置文件; 2 mybatis的mapper文件

  1. mybatis主配置文件,提供mybatis全局设置的。包含的内容 日志, 数据源,mapper文件位置
  2. mapper文件: 写sql语句的。 一个表一个mapper文件

    settings部分

    settings是mybatis的全局设置,影响整个mybatis的运行。 这个设置一般使用默认值就可以了。
    <settings>
    <setting name="cacheEnabled" value="true"/>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="multipleResultSetsEnabled" value="true"/>
    <setting name="useColumnLabel" value="true"/>
    <setting name="useGeneratedKeys" value="false"/>
    <setting name="autoMappingBehavior" value="PARTIAL"/>
    <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
    <setting name="defaultExecutorType" value="SIMPLE"/>
    <setting name="defaultStatementTimeout" value="25"/>
    <setting name="defaultFetchSize" value="100"/>
    <setting name="safeRowBoundsEnabled" value="false"/>
    <setting name="mapUnderscoreToCamelCase" value="false"/>
    <setting name="localCacheScope" value="SESSION"/>
    <setting name="jdbcTypeForNull" value="OTHER"/>
    <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
    </settings>
    
    图片.png

    配置环境

    ```xml environments: 环境标签, 在他里面可以配置多个environment
    属性: default ,必须是某个environment的id属性值。 表示mybatis默认连接的数据库
    
    environment: 表示一个数据库的连接信息。
    属性: id 自定义的环境的标识。 唯一值。
    
    transactionManager:事务管理器
    属性:  type 表示事务管理器的类型。
    属性值:1)JDBC: 使用Connection对象, 由mybatis自己完成事务的处理。
          2) MANAGED: 管理,表示把事务的处理交给容器实现(由其他软件完成事务的提交,回滚)
    
    dataSource: 数据源,创建的Connection对象,连接数据库。
    属性:  type 数据源的类型
    属性值:1) POOLED, mybatis会在内存中创建PooledDataSource类,管理多个Connection连接对象,使                           用的连接池
          2) UNPOOLED ,不使用连接池, mybatis创建一个UnPooledDataSource这个类, 每次执行sql                           语句先创建Connection对象,再执行sql语句,最后关闭Connection
          3) JNDI : java的命名和目录服务。
    
<a name="iCJKa"></a> ## TypeAliases别名 mybatis提供的对java类型定义简短,好记名称。<br />自定义别名的步骤:<br />1)在mybatis主配置文件,使用 typeAliases标签声明别名<br />2)在mapper文件中, resultType="别名"<br />声明别名(mybatis主配置文件)xml mapper文件中使用xml resultType=”别名” ![图片.png](https://cdn.nlark.com/yuque/0/2021/png/2442606/1627282158820-205de9e8-c766-4e38-b0dc-8ccc608f41b0.png#clientId=u95155109-b06c-4&from=paste&height=238&id=ufdbd13d8&margin=%5Bobject%20Object%5D&name=%E5%9B%BE%E7%89%87.png&originHeight=475&originWidth=767&originalType=binary&ratio=1&size=80899&status=done&style=none&taskId=u7d96f3b8-c352-4207-8fd5-504d33f841d&width=383.5) <a name="dzC4K"></a> ## 使用数据库属性配置文件 需要把数据库的配置信息放到一个单独文件中, 独立管理。 这个文件扩展名是 properties. 在这个文件中,使用自定义的key=value的格式表示数据<br />使用步骤:<br />1.在resources目录中,创建xxxx.properties<br />2.在文件中,使用 key=value的格式定义数据。<br />例如 jdbc.url=jdbc:mysq://localhost:3306/springdb<br />3.在mybatis主配置文件, 使用properties标签引用外部的属性配置文件<br />4.在使用值的位置, 使用${key}获取key对应的value(等号右侧的值)<br />例子:<br />jdbc.propertiesxml jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=utf-8 jdbc.username=root jdbc.password=123 mybatis主配置文件xml <a name="rRW2W"></a> ## enviroments环境 - MyBatis可以配置多种环境,比如开发、测试和生 产环境需要有不同的配置。 - 每种环境使用一个environment标签进行配置并指 定唯一标识符 - **可以通过environments标签中的default属性指定 一个环境的标识符来快速的切换环境** - id:指定当前环境的唯一标识,transactionManager、和dataSource都必须有 <a name="rQJQ6"></a> ### transactionManager type: JDBC | MANAGED | 自定义 - JDBC:使用了 JDBC 的提交和回滚设置,依赖于从数 据源得到的连接来管理事务范围。 JdbcTransactionFactory - MANAGED:不提交或回滚一个连接、让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下 文)。 ManagedTransactionFactory - 自定义:实现TransactionFactory接口,type=全类名/ 别名 <a name="TgIUa"></a> ### dataSource type: UNPOOLED | POOLED | JNDI | 自定义 - UNPOOLED:不使用连接池, UnpooledDataSourceFactory - POOLED:使用连接池, PooledDataSourceFactory - JNDI: 在EJB 或应用服务器这类容器中查找指定的数据源 - 自定义:实现DataSourceFactory接口,定义数据源的获取方式。 实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置java
<a name="PjZ2B"></a>
## databaseIdProvider环境

- MyBatis可以根据不同的数据库厂商执行不同的语句
```java
<databaseIdProvider type="DB_VENDOR">
    <property name="MySQL" value="mysql"/>
    <property name="Oracel" value="oracel"/>
</databaseIdProvider>

Type: DB_VENDOR 使用MyBatis提供的VendorDatabaseIdProvider解析数据库 厂商标识。也可以实现DatabaseIdProvider接口来自定义。

  • Property-name:数据库厂商标识
  • Property-value:为标识起一个别名,方便SQL语句使用databaseId属性引用

图片.png

mapper 映射

使用mapper指定其他mapper文件的位置,
mapper标签使用的格式有两个常用的方式:

<mappers>
    <!--第一种方式, resources="mapper文件的路径"
        优点:文件清晰。 加载的文件是明确的。
              文件的位置比较灵活。
        缺点:文件比较多, 代码量会比较大, 管理难度大
    -->
    <mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
    <mapper resource="com/bjpowernode/dao/OrderDao.xml"/>
    <!--
       第二种方式,使用<package>
       name:包名, mapper文件所在的包名。
       特点: 把这个包中的所有mapper文件,一次加载。

       使用要求:
        1. mapper文件和dao接口在同一目录
        2. mapper文件和dao接口名称完全一样。
    -->
    <package name="com.bjpowernode.dao" />
    <package name="com.bjpowernode.dao1" />
</mappers>

图片.png

缓存

MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。MyBatis系统中默认定义了两级缓存。
一级缓存和二级缓存

  • 1、默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
  • 2、二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
  • 3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存

一级缓存:本地缓存,与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。
二级缓存:全局缓存

一级缓存

一级缓存(local cache), 即本地缓存, 作用域默认为sqlSession。当 Session flush 或 close 后, 该Session 中的所有 Cache 将被清空。
本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
在mybatis3.1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配置

一级缓存失效的四种情况

1、不同的SqlSession对应不同的一级缓存
2、同一个SqlSession但是查询条件不同
3、同一个SqlSession两次查询期间执行了任何一次增删改操作
4、同一个SqlSession两次查询期间手动清空了缓存

二级缓存

基于namespace级别的缓存,一个namespace对应一个二级缓存
工作机制
1.一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中
2.如果会话关闭,一级缓存的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存
3.不同的namespace查出的数据会放在自己对应的缓存中(map)
使用
1.开启全局二级缓存的配置
2.去mapper.xml中配置二级缓存

  • eviction:缓存回收策略

LRU:最近最少使用的,移除最长时间不被使用的对象
FIFO:先进先出,按对象进入缓存的顺序来移除他们
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK:弱引用,更积极的移除基于垃圾收集器状态和弱引用规则的对象
默认是LRU

  • flushInterval:缓存刷新时间间隔
    • 缓存多长时间清空一次,默认不清空,设置一个毫秒值
  • readOnly:是否只读
    • true:只读,mybatis会认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
    • false:非只读,mybatis觉得获取的数据可能会被修改。mybatis会利用序列化和反序列化的技术克隆一份新的数据给你。安全,速度慢
  • size:缓存存放多少元素
  • type:指定自定义缓存的全类名,实现Cache接口即可
  1. POJO需要实现序列化接口

缓存使用细节

  1. 每个select标签中都会userCache=”true”属性,表示是否使用缓存
  2. 每个增删改标签都会有flushChche=”true”,增删改完成会就会清除缓存
  3. sqlSession.clearCache(),只是清楚当前Session的一级缓存
  4. localCacheScope:本地缓存作用域:(一级缓存Session),当前会话的所有数据保存在会话缓存中

MyBatis逆向工程

是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删改查,以及QBC风格的条件查询。但是表连接、存储过程等这些复杂sql的定义需要我们手工编写。
官方地址
添加mbg.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

    <!-- 
        targetRuntime="MyBatis3Simple":生成简单版的CRUD
        MyBatis3:豪华版

     -->
  <context id="DB2Tables" targetRuntime="MyBatis3">
      <!-- jdbcConnection:指定如何连接到目标数据库 -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/test?allowMultiQueries=true"
        userId="root"
        password="liuhuande123">
    </jdbcConnection>

    <!-- 类型解析器 -->
    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

    <!-- javaModelGenerator:指定javaBean的生成策略 
    targetPackage="test.model":目标包名
    targetProject="\MBGTestProject\src":目标工程
    -->
    <javaModelGenerator targetPackage="main.java.cn.itcast.domain"
            targetProject=".\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <!-- sqlMapGenerator:sql映射生成策略: -->
    <sqlMapGenerator targetPackage="main.java.cn.itcast.dao"
        targetProject=".\src">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

    <!-- javaClientGenerator:指定mapper接口所在的位置 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="main.java.cn.itcast.dao"
        targetProject=".\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

    <!-- 指定要逆向分析哪些表:根据表要创建javaBean -->
    <table tableName="tbl_dept" domainObjectName="Department"></table>
    <table tableName="tbl_employee" domainObjectName="Employee"></table>
  </context>
</generatorConfiguration>

运行生成代码

@Test
    public void  testMbg() throws Exception {
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }

运行原理

插件

拓展

PageHelper

PageHelper做数据分页。 在你的select语句后面加入 分页的 sql 内容, 如果你使用的mysql数据库, 它就是在select * from student 后面加入 limit 语句。
使用步骤:
1.加入依赖pagehelper依赖

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

2.在mybatis主配置文件, 加入plugin声明

在<environments> 之前加入
<plugins>
   <plugin interceptor ="com.github.pagehelper.PageInterceptor" />
</plugins>

3.在select语句之前,调用PageHelper.startPage(页码, 每页大小)
对比:
没有使用PageHelper
select from student order by id
使用PageHelper
SELECT count(0) FROM student
select
from student order by id LIMIT ?