04-MyBatis【框架开发、mybatis配置文件标签、XxxMapper映射文件标签、动态sql、高级查询】

文章目录

一、框架

1、框架介绍

程序开发中的框架往往是对常见功能的封装通常与具体业务无关

也可以认为是软件的半成品。

程序框架理解为基础或者机械标准件(例如螺丝螺母标准的机械部件)。

一个框架是一组可复用的设计构件。

框架是一个半成品,软件是成品。我们在它的基础上开发出成品(软件)。

2、框架的作用

1.解决了技术通用的问题

在JavaEE体系中,有着各种各样的技术。不同的软件企业,根据自身的业务需求选择不同的技术,容易造成应用依赖技术,增加了项目开发实现的复杂性和技术风险性。而框架技术就可以解决上述问题。

2.提升了开发效率

企业项目中使用框架,只需要专注实现业务需求。使用框架的方便性,提升了开发效率

3.提升了系统稳定性

一个成熟的框架,经过了在众多企业项目中的验证使用,稳定性有保障

二、MyBatis框架

**官网及框架包下载:**http://www.mybatis.org/mybatis-3/

1、mybatis框架介绍

	`Mybatis是Apache软件基金会下的一个开源项目`,前身是iBatis框架。2010年这个项目由 apache 软件基金会迁移到google code下,改名为mybatis。2013年11月又迁移到了 github(GitHub 是一个面向开源及私有 软件项目的托管平台)。
	
    MyBatis 是一款优秀的`持久层框架`,它支持定制化 SQL、存储过程以及高级映射(多表)`MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集`。它对 jdbc 的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建 connection、创建 statement、手动设置参数、结果集检索等 jdbc 繁杂的过程代码。
    
    MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的` POJO(Plain Old Java Objects,普通老式 Java 对象)`为数据库中的记录。
1)mybatis的优点
  1. 简单易学:mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个SQL映射文件即可。

  2. 使用灵活:Mybatis不会对应用程序或者数据库的现有设计强加任何影响。SQL语句写在XML里,便于统一管理和优化。

  3. 解除SQL与程序代码的耦合通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易进行单元测试。SQL语句和代码的分离,提高了可维护性。

2)mybatis的不足
  1. 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。

  2. SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。

  3. Mybatis框架还是比较简陋(半自动化框架),功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改

3)mybatis整体架构

在这里插入图片描述

4) mybatis的ORM方式

在这里插入图片描述

Object Relational Mapping 对象关系映射

Mybatis有两种映射方式:

1.通过XML映射;

2.通过注解;

2、mybatis框架开发

Mybatis存在2种开发方式

  1. 旧版本mybatis执行的方式(了解即可)

  2. 动态代理实现操纵数据库(掌握)

代码演示

0)Mybatis环境搭建

1.创建工程
2.导入依赖jar包;
3.创建接口和实体类;
4.配置mybatis全局配置文件和映射文件;
5.测试;

-- 数据准备
create table user (
    id int primary key auto_increment,
    username varchar(20) not null,
    birthday date,
    sex char(1) default '男',
    address varchar(50)
);
insert into user values (null, '孙悟空','1980-10-24','男','花果山水帘洞');
insert into user values (null, '白骨精','1992-11-12','女','白虎岭白骨洞');
insert into user values (null, '猪八戒','1983-05-20','男','福临山云栈洞');
insert into user values (null, '蜘蛛精','1995-03-22','女','盤丝洞');

select * from user;
1)mybatis入门(老式)开发【了解】

开发步骤:

第1步:创建项目工程

第2步:基于数据表编写实体类

第3步:编写mybatis配置文件 mybatis‐config.xml

第4步:编写映射文件 XxxMapper.xml

第5步:书写测试类

1、mybatis解决了三层(web,service,dao)中哪一层的问题?
dao
2、mybatis框架是对什么技术进行的封装?
JDBC

在这里插入图片描述

User类
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
    
//getter and setter and toString
}
mybatis-config.xml

【在src下创建核心配置文件:mybatis‐config.xml】

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <!--配置数据库环境-->
        <environment id="development">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
                 <property name="driver" value="com.mysql.jdbc.Driver"/>
                 <property name="url" value="jdbc:mysql:///dbname"/>
                 <property name="username" value="root"/>
                 <property name="password" value="1234"/>
             </dataSource>
         </environment>
     </environments>

    <mappers>
    <!--引入映射文件-->
    <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>
UserMapper.xml

【在src下创建映射文件UserMapper.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="org.example.UserMapper">
     <select id="selectUser" resultType="com.testmybatis.pojo.User">
     select * from user where id = #{id}
     </select>
</mapper>
MybatisTest测试类
public class TestAll {
    @Test
    public void test1() throws IOException {
        //1.通过sql会话工厂构建类加载核心配置文件构建会话工厂
        String resource="mybatis-config.xml";
        InputStream in = Resources.getResourceAsStream(resource);
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
        //2.获取SqlSession会话对象 底层是对Connection封装
        SqlSession sqlSession = sessionFactory.openSession();
        //3.调用会话 api完成查询
        User user = sqlSession.selectOne("org.example.UserMapper.selectUser", 1);
        System.out.println(user);
        //4.关闭资源
        sqlSession.close();
     }
   
}
log4j.properties 日志
### 设置Logger输出级别和输出目的地 ###
log4j.rootLogger=debug, stdout
### 把日志信息输出到控制台 ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout

Mybatis框架的核心类有哪些?

SqlSessionFactoryBuilder 会话工厂构建类 主要用来构建会话工厂的

SqlSessionFactory 会话工厂类 主要用来创建会话对象的;

SqlSession 会话对象 底层是对Connection连接对象的封装;

Mybatis老式的开发模式弊端

 要使用指定的sql必须使用命名空间id进行字符串拼接,维护性比较差

 并且sql用多少次,就拼接多少次,维护性比较差

2)mybatis的dao层(持久层)动态代理实现【重点】
Mybatis动态代理开发流程

1)导入依赖的jar (mybatis.jar 、jdbc驱动包 、log4j.jar)

2)定义pojo类型 (实体类)

3)定义操纵pojo类的接口

4)定义xml映射文件,然后与接口进行绑定

​ 4.1 xml的命名空间与接口的全限定名称要一致

​ 4.2 xml中的crud的标签id与接口中的方法名称要一致

5)配置核心配置文件 (1.配置数据源 2.加载xml映射文件)

6)通过SqlSessionFactoryBuilder类加载核心配置文件,构建会话工厂

7)通过会话工厂获取会话对象

8)通过会话对象调用**getMapper(接口名.class)**获取代理对象(正常使用接口完成数据库的操作)

9)释放资源,关闭连接session.close()

核心步骤

1)接口与xml映射文件绑定

接口名称与xml映射文件命名空间要一致

接口方法与xml中crud标签ID要一致

2)通过SqlSession获取代理对象

​ 例如:sqlSession.getMapper(接口)

在这里插入图片描述

案例演示

用mybatis动态代理根据用户ID查询用户信息

UserMapper接口

第一步:新建接口

/**
* 根据用户id查询用户信息
* @param id
* 接口名称与xml映射文件命名空间要一致
* 接口方法与xml中crud标签ID要一致
*/
public interface UserMapper {
	User findUserById(Integer id);
}
UserMapper.xml

第二步:将接口和映射文件绑定,在映射文件中书写SQL语句

接口与xml映射文件进行绑定**【注意】**

  1. 接口的全名称(带有包名)与xml的命名空间名称要一致
  2. 接口中的方法名称与xml的select标签的id要一致;
<?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.testmybatis.mapper.UserMapper">
     <select id="findUserById" resultType="com.heima.pojo.User">
        select * from user where id = #{id}
     </select>
</mapper>

MybatisTest测试类

第三步:获取UserMapper的动态代理对象 完成查询

@Test
public void test2(){
    // 1.加载核心配置文件,构建会话工厂
    InputStream in = null;
    try {
     	in = Resources.getResourceAsStream("mybatis-config.xml");
     } catch (IOException e) {
     	e.printStackTrace();
     }
    // 获取会话工厂构建类对象
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
    
    // 2.获取会话对象
    SqlSession sqlSession = sessionFactory.openSession();
    // 3.获取接口代理对象
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.findUserById(1);
    System.out.println(user);
    // 4.关闭资源
    sqlSession.close();
}

3)mybatis操作事务
    @Test
    public  void test05() throws IOException {
        //基于mybatis核心配置文件,获取会话工厂对象
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new 	SqlSessionFactoryBuilder().build(inputStream);

        //利用SqlSessionFactory,获取SqlSession
		// 重载 SqlSession openSession(boolean var1);
        //默认事务:手动提交   自动提交:设为true 
        SqlSession sqlSession = sqlSessionFactory.openSession(); // 使用默认事务:手动提交  
//使用SqlSession执行sql
        /*
            <!-- 向数据表中插入数据 -->
            <insert id="addUser">
                insert into user(user_name,birthday,sex,address)
                values (#{userName},#{birthday},#{sex},#{address})
            </insert>
         */
        // 获取接口代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        User u = new User(0,"白龙马",
                java.sql.Date.valueOf("1998-9-30"),"男","东海");
        // 根据 id=""  UserMapper调用接口中的方法 执行 sql 语句
        int rowCount = userMapper.addUser(u);  // 代理对象调用方法 被代理的方法进行增强

        //处理结果
        if(rowCount>0){
            sqlSession.commit();  // 手动提交事务
            System.out.println("插入成功!");
        }

        //关闭
        sqlSession.close();
    }
4)编写会话工具类

在静态代码块中创建会话工厂对象

编写静态方法得到会话对象

编写静态方法得到会话工厂对象

public class MybatisUtil {
    //全局维护一个会话工厂
    private static SqlSessionFactory sqlSessionFactory;

    private static ThreadLocal<SqlSession> threadLocal=new ThreadLocal<>();


    // 1、在静态代码块中加载mybatis核心配置文件,初始化sqlSessionFactory
    static {
        //抽取初始化工厂的方法
        initFactory();
    }
    
    // 2、 封装初始化session工厂的方法
    private static void initFactory() {
        String resource="mybatis-config.xml";
        InputStream in = null;
        try {
            in = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory= new SqlSessionFactoryBuilder().build(in);
    }

    /**
     * 获取sqlSession对象
     * @return
     */
    // 3、静态方法得到会话对象
    private static SqlSession getSqlSession(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession==null){
            sqlSession=sqlSessionFactory.openSession();
            //放回线程中 通过threadLocal为每一个线程都维护一个私有的会话对象,防止出现并发问题
            threadLocal.set(sqlSession);
        }
        return sqlSession;
    }

    /**
     * 封装获取动态代理对象的方法
     * @param tClass
     * @param <T>
     * @return
     */
    public static  <T> T getMapper(Class<T> tClass){
        SqlSession sqlSession = getSqlSession();
        T mapper = sqlSession.getMapper(tClass);
        return mapper;
    }

    /**
     * 关闭资源
     */
    public static void close(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession!=null){
            sqlSession.close();
        }
    }

    /**
     * 事务提交
     */
    public static void commit(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession!=null){
            sqlSession.commit();
        }
    }
    /**
     * 事务回滚
     */
    public static void rollback(){
        SqlSession sqlSession = threadLocal.get();
        if(sqlSession!=null){
            sqlSession.rollback();
        }
    }
}

3、mybatis核心(标签)配置

mybatis全局配置介绍

mybatis-confifig.xml,是MyBatis的全局配置文件,包含全局配置信息,如数据库连接参数、插件等。整个框架中只需要一个即可

说明:上述标签在实际使用过程中,要严格遵循使用顺序,否则报错

参考:https://mybatis.org/mybatis-3/zh/configuration.html

1)properties(引入外部文件)

**properties标签作用:**定义全局变量

properties定义全局变量2中方式

1 内部定义
<properties>
<property name="key" value=""/>
</properties>

2 外部引入(推荐)
<properties resource="外部配置文件路径"/>
<properties>标签有两种使用方式
properties方式1

通过properties的子标签设置属性; 使用**${key}获取设置的属性值**;

<?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下的内部配置,配置全局参数 (了解即可)】
     <properties>
         <property name="driverClass" value="com.mysql.jdbc.Driver"/>
         <property name="dburl" value="jdbc:mysql://127.0.0.1:3306/db1"/>
         <property name="username" value="root"/>
         <property name="passwd" value="1234"/>
     </properties>
---------------------------------------------------------------       
    <environments default="dev">
        <!--配置数据库环境-->
        <environment id="dev">
             <transactionManager type="JDBC"/>
             <dataSource type="POOLED">
                 【使用${key}获取设置的属性值】
             <property name="driver" value="${driverClass}"/>
             <property name="url" value="${dburl}"/>
             <property name="username" value="${username}"/>
             <property name="password" value="${passwd}"/>
             </dataSource>
         </environment>
     </environments>

    <mappers>
    <!--引入映射文件-->
    <mapper resource="UserMapper.xml"/>
    </mappers>
</configuration>
properties方式2

通过properties标签 resource属性引入加载外部properties文件

首先创建jdbc.properties配置文件

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///day06_1
jdbc.userName=root
jdbc.password=1234

properties引入外部的配置文件, 加载value值

引入外部的配置文件
<properties resource="jdbc.properties"/>

加载value值 
<dataSource type="POOLED">
     <property name="driver" value="${jdbc.driverClass}"/>
     <property name="url" value="${jdbc.url}"/>
     <property name="username" value="${jdbc.userName}"/>
     <property name="password" value="${jdbc.password}"/>
</dataSource>

修改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文件 -->
    <properties resource="jdbc.properties"/>
---------------------------------------------------------------       
    <environments default="dev">
    <!--配置数据库环境-->
    <environment id="dev">
         <transactionManager type="JDBC"/>
         <dataSource type="POOLED">
         ---------------------------------------------------------------   
         <property name="driver" value="${jdbc.driverClass}"/>
         <property name="url" value="${jdbc.url}"/>
         <property name="username" value="${jdbc.userName}"/>
         <property name="password" value="${jdbc.password}"/>
         ---------------------------------------------------------------   
         </dataSource>
         </environment>
     </environments>
    
     <mappers>
        <!--引入映射文件-->
        <mapper resource="UserMapper.xml"/>
     </mappers>
</configuration>
2)settings(设置MyBatis 运行时行为)

settinngs是 MyBatis 中极为重要的调整设置,

它们会改变 MyBatis 的运行时行为。

说明:

settings参数有很多,我们先学习驼峰匹配mapUnderscoreToCamelCase

就是映射下划线到驼峰式命在这里插入图片描述
自动把数据表中带有下划线的字段,转换为驼峰式命名

修改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文件 -->
<properties resource="jdbc.properties"/>
    
 <settings>
<!--开启驼峰映射:
 table字段名称:user_name 自动变成:username或 userName
 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
 </settings>
---------------------------------------------------------------       
    <environments default="dev">
         <!--配置数据库环境-->
         <environment id="dev">
         <transactionManager type="JDBC"/>
         <dataSource type="POOLED">
             <property name="driver" value="${jdbc.driverClass}"/>
             <property name="url" value="${jdbc.url}"/>
             <property name="username" value="${jdbc.userName}"/>
             <property name="password" value="${jdbc.password}"/>
         </dataSource>
         </environment>
     </environments>
    
    <mappers>
    <!--引入映射文件-->
    <mapper resource="UserMapper.xml"/>
     </mappers>
</configuration>

开启驼峰自动映射的配置和作用

  1. 配置

  1. 作用

自动将表中字段比如:user_name 映射到pojo属性:username/userName

无需给sql中字段取别名

3)typeAliases(类型别名)

类型别名是给类的全限定名称(包名.类名))取一个短名称。

作用:减少类完全限定名的冗余。

在这里插入图片描述

通过设置一些短名来代替全限定名,有两种方式

方式1:使用typeAliases标签的子标签【 typeAlias手动设置类型别名】(逐一设置维护)

方式2:使用typeAliases标签的子标签 【package包扫描映射别名】(推荐)

代码演示:使用typeAliases标签的子标签 typeAlias简化UserMapper.xml中pojo类的全限定名称

首先 修改MyBatis-config.xml配置文件

<configuration>
 
<!-- 引入外部的properties文件 -->
<properties resource="jdbc.properties"/>
    
 <settings>
<!--开启驼峰映射:
 table字段名称:user_name 自动变成:username或 userName
 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
    
---------------------------------------------------------------  
<!--方式1:取别名,逐个取别名-->
<typeAliases>
    <typeAlias type="com.itheima.pojo.User" alias="user"/> 逐个取别名
</typeAliases>
--------------------------------------------------------------
 <!-- 方式2:通过扫包取指定包下的pojo类取别名 (推荐) -->
 <typeAliases>
	<!-- package name指定要取别名的包名 -->   对该包下的所有类取别名
    <package name="com.itheima.pojo"/>  例如:自动设置别名为 User/user
</typeAliases>
---------------------------------------------------------------       
    <environments default="dev">
         <!--配置数据库环境-->
         <environment id="dev">
         <transactionManager type="JDBC"/>

             
         ......................
         ......................
         ......................
 </configuration>

其次 简化UserMapper.xml文件中pojo类的全限定名称

<?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.testmybatis.mapper.UserMapper">
    ---------------------------------------------
     <select id="findUserById" resultType="user">  // resultType返回类型      
     对比           
     <select id="findUserById" resultType="com.heima.pojo.User">  // 包名+类名
      
    ---------------------------------------------  
        select * from user where id = #{id}
     </select>
</mapper>

**代码演示:**完成根据id查询用户姓名的功能

1)定义接口方法:
/**
 * 练习3:完成根据id查询用户姓名的功能;
 * @param id
* @return
 */
String findNameByUserId(Integer id);
2)接口方法与xml映射文件sql进行绑定
<!--此时resultType中参数string使用的时String类的默认别名-->
<select id="findNameByUserId" resultType="string">
 	select user_name from user where id=#{id}
</select>
3)测试
public void test3(){
    //1.加载核心配置文件,构建会话工厂
    InputStream in = null;
    try {
     in = Resources.getResourceAsStream("mybatis-config.xml");
     } catch (IOException e) {
     e.printStackTrace();
     }
    SqlSessionFactory sessionFactory = new 
    SqlSessionFactoryBuilder().build(in);
    //2.获取会话对象
    SqlSession sqlSession = sessionFactory.openSession();
    //3.获取接口代理对象
    UserMapper mapper = 
    sqlSession.getMapper(UserMapper.class);
    String name = mapper.findNameByUserId(1);
    System.out.println(name);
    //4.关闭资源
    sqlSession.close();
}
4)typeHandlers(类型处理器)

MyBatis 在设置预处理语句PreparedStatement)中的参数或从结果集中取出一个值时,

用类型处理器将获取到的值以合适的方式转换成 Java 类型。

Mybatis内置的一些类型处理器,企业开发中使用默认的类型处理器;

在这里插入图片描述

5) environments(环境配置)

 MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置

 尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一

 虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离

父标签: environments(环境配置)

子标签:

​ environment(环境变量)

​ transactionManager(事务管理器)

​ dataSource(数据源)

案例:xml配置dev、test、online多环境数据源,并指定test为默认环境运行

在这里插入图片描述

6)mappers(映射器)

Mappers标签作用:提供了关联加载xml映射文件的配置功能

两种使用方式

1、加载XML映射文件,关联UserMapper.java接口
【方式1<mapper resource="UserMapper.xml"/>   // 从src下加载映射文件
【方式2<mapper url=""/>  		// 从本地磁盘中加载映射文件,但是需要添加`file:///`协议
/** 说明:如果项目采用基于xml的开发模式,建议使用方式1开发 */

2、加载接口,关联映射文件
条件:1、接口名和映射文件名保持一致;  2、路径保持一致;
【方式3<mapper class=“com.test.mybatis.dao.UserMapper”/>      //加载接口
【方式4<package name="com.test.mybatis.dao"/>  				//批量加载class
/** 说明:如果基于注解开发的开发的话,推荐使用方式4开发 */

代码演示

1) 使用mapper下resource属性加载xml映射文件关联接口(掌握)
<mappers>
    <!--通过resource属性加载工程下相对路径下的映射文件--> 
    <mapper resource="mapper/UserMapper.xml"/>       // 从src下加载映射文件
</mappers>

2) 使用完全限定资源符加载映射文件(了解即可) <mapper url="file:///E:\day06\day06_1\src\mapper\UserMapper.xml"/>

3) 使用映射器接口实现类的完全限定类名 (了解即可)
前提条件:
1、接口名和映射文件名保持一致;
2、路径保持一致;
3、映射文件中的namespace必须与接口的全限定名称一致; 
<mapper class="com.itcast.mapper.UserMapper"/>

4) 加载接口,关联映射文件方式package(掌握)
前提条件:
1、接口名和映射文件名保持一致;
2、路径保持一致;
3、映射文件中的namespace必须与接口的全限定名称一致; 
<package name="com.itcast.mapper"/>      // 接口的包路径   批量加载class

1、开发时mybatis-config.xml中常用的2种加载映射文件XxxMapper.xml的方式

①通过resource属性,相对路径加载(基于xml开发推荐的)

②通过package扫包方式(基于注解开发推荐使用)

约束条件:

1 接口名称与xml映射文件名称要一致;

2 接口路径与xml映射文件路径也要一致;

===================================================

2、基于package扫描的原理?

前提:

1.接口名称与xml映射文件名称要一致;

2.接口路径与xml映射文件路径也要一致;

通过package指定接口的路径之后,mybatis就会加载这个包下的接口,获取

接口的名称,因为接口名称和路径与xml一致,所以也就获取xml映射文件;

===================================================

3、思考:基于package扫描存在的缺点?

  1. 约束条件太多了;

  2. java文件与xml文件存放在同一个路径下,可维护性差;

4、Mybatis映射文件配置

 Mapper映射文件中定义了操作数据库的sql,每一个sql都被包含在一个statement中

 映射文件是mybatis操作数据库的核心

SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

在这里插入图片描述

说明:

映射文件中需要直接书写SQL语句对数据库进行操作,对数据库操作SQL语句主要有CRUD四类。

这四类对应到映射文件中的配置为四类标签:selectinsert,updatedelete

1)slelect标签

在这里插入图片描述

根据id查询用户生日

1) 定义接口方法
    
/**
 * 根据id查询用户生日
 * @param uid
* @return
*/
public abstract Date findBirthdayById(int uid);


2) xml文件sql绑定接口方法
<!-- 根据用户id查询用户生日 -->
<select id="findBirthdayById" resultType="java.sql.Date">
 	select birthday from user where id = #{id}
</select>

    
3) 测试
@Test
public void test01() throws IOException {
    //1.通过sql会话工厂构建类加载核心配置文件构建会话工厂
    String resource = "mybatis-config.xml";
     InputStream in = Resources.getResourceAsStream(resource);
     SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(in);
    //2.获取SqlSession会话对象 底层是对Connection封装
    SqlSession sqlSession = sessionFactory.openSession();
    //3. 获取接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
     Date birthday = userMapper.findBirthdayById(1);
     System.out.println(birthday);
    //4. 关闭资源
    sqlSession.close();
}
2)insert标签

在这里插入图片描述

在这里插入图片描述

向数据库添加用户

1) 定义接口方法:
/**
 * 添加用户
* 返回值时影响的行数
* @param user
* @return
 */
Integer addUser(User user);


2) 绑定映射文件
<!--比如:拿到username 进行方法组装getUsername-->
<!--结论:如果传入的时pojo对象,那么参数名称与pojo的属性名称要一致-->
<insert id="addUser">
 	insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert
    
    
3) 测试
@Test
public void test02() throws IOException {
    //1.通过sql会话工厂构建类加载核心配置文件构建会话工厂
    String resource = "mybatis-config.xml";
     InputStream in = Resources.getResourceAsStream(resource);
     SqlSessionFactory sessionFactory = new 
    SqlSessionFactoryBuilder().build(in);
    //2.获取SqlSession会话对象 底层是对Connection封装
    SqlSession sqlSession = sessionFactory.openSession();
    //3. 获取接口的代理对象
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //调用方法,添加用户
     int rowCount = userMapper.addUser(new User(null, "唐憎", 
     Date.valueOf("2020-12-15"), "男", "长安"));
     System.out.println(rowCount);
    //手动提交事务(MyBatis默认事务为手动提交)
    sqlSession.commit();
    //4. 关闭资源
    sqlSession.close();
}
3)update标签
1) 定义接口方法
/**
 * 更新用户
* @param user
* @return
 */
void updateUser(User user);


2) 绑定映射文件
<update id="updateUser">
 update user set user_name=#{username},birthday=#{birthday},sex=#{sex},address=
 #{address} where id=#{id}
</update>
4)delecte
1) 定义接口方法
/**
 * 根据id删除用户
* @param id
*/
void deleteById(Integer id);

2) 映射文件绑定接口方法
<delete id="deleteById">
 	delete from user where id=#{id}
</delete>

小结

MyBatis框架:

  • 框架一个半成品
  • 程序是在一个半成品的项目上开发程序
    • 学习半成品项目怎么使用(学习怎么使用MyBatis)

MyBatis的使用流程:

  • 1、创建项目
  • 2、在项目中导入mybatis的jar文件(mybatis是dao层框架,需和数据库交互,需要数据库的jar文件)
  • 3、编写核心配置文件: mybatis-config.xml (mybatis加载时要使用)
    • 连接数据库要使用的参数:驱动、url、登录名、密码
    • mybatis框架使用的全局配置:properties、settings、typeAliases
  • 4、编写实体类
  • 5、编写映射文件:XxxxMapper.xml (用来书写sql语句)
  • 6、创建一个和映射文件关联的接口
    • 映射文件中的 名称空间的值 = 接口的全名称
    • 映射文件中的 CRUD标签的id值 = 接口中的方法名
  • 6、获取MyBabtis中的SqlSession对象(Connection、CRUD操作)
    • 使用SqlSessionFactory对象,获取SqlSession对象
  • 7、利用SqlSession对象,执行CRUD的sql语句
  • 8、处理sql执行结果
  • 9、关闭SqlSession

相对路径问题

mybatis-config.xml文件
<!-- 1、使用Properties标符,引入外部的资源文件 -->
<properties resource="config/jdbc.properties"/>

<package name="com.itheima.mb.mapper"/>
<mapper resource="config/UserMapper.xml"/>
------------------------------------------------------
UserMapper.xml文件
<!--// 命名空间为 映射文件的相对src目录的路径  【加载映射接口,使用其中抽象方法】 -->
<mapper namespace="com.itheima.mb.mapper.UserMapper">
------------------------------------------------------    
  测试方法中:  
    String resource = "config/mybatis-config.xml";   // 相对于 src 下的相对路径
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new 		SqlSessionFactoryBuilder().build(inputStream);
一般mybatis-config.xml配置文件
1)核心配置文件配置标签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>
    <!-- 1、使用Properties标符,引入外部的资源文件 -->
    <properties resource="jdbc.properties"/>

    <!--2、mapUnderscoreToCamelCase自动把带有下划线的字段名,转换为驼峰命名 -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!-- 3、这里使用package标签 : 指定包下的所有类型,都具有别名-->
    <!--<typeAlias type="com.itheima.mb.pojo.User" alias="User"/>-->
    <!-- 需要给指定的类型,取别名。 如果有多个指定类型时,需要书写多个typeAlia  -->
    <!-- 使用package,指定包名。 指定包下的所有类型,都具有别名
    eg: com.pojo.User     com.pojo.Student
    指定包名:com.pojo    那么【 com.pojo.User 】的别名为: user 或  User
     -->
    <typeAliases>
        <package name="com.itheima.mb.pojo"/>
    </typeAliases>

    <!-- 4、environments指定开发环境dev / test-->
    <environments default="dev">
        <!-- 1. 开发数据库环境-->
        <environment id="dev">
            <!-- 1.1 事务管理器: 数据库中的事务由JDBC完成 -->
            <transactionManager type="JDBC"/>
            <!-- 1.2 type="POOLED" 表示: 使用mybaits默认的连接池 -->
            <dataSource type="POOLED">
                <!-- 1.3 给(属性)参数赋值 :${key} 获取到jdbc.properties属性集配置文件参数Value值 -->
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.userName}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <!--2. 测试数据库环境-->
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.userName}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

    </environments>
    
    <!-- 5、这里使用package标签:把指定接口下所有的接口引入  适用于:注解方式 -->
    <!-- 把指定接口下所有的接口引入
    加载接口,关联映射文件方式package(掌握)
    <package name="com.itheima.mb.mapper"/>   接口的包路径
    前提条件:
    1、接口名和映射文件名保持一致;
    2、路径保持一致;
    3、映射文件中的namespace必须与接口的全限定名称一致;
    适用于:注解方式 -->
    <!-- 引入映射文件    适用于:  xml方式
    <mapper resource="com/itheima/mb/mapper/UserMapper.xml"/>  文件的相关路径
    <mapper class="com.itheima.mb.mapper.UserMapper" />  接口实现类的完全限定类名
    -->
    <mappers>
        <package name="com.itheima.mb.mapper"/>
    </mappers>

</configuration>
一般XxxMapper.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">

<!--// 命名空间为 映射文件的相对src目录的路径  【加载映射接口,使用其中抽象方法】 -->
<mapper namespace="cn.mybatis.demo.mapper.UserMapper">

    <!-- 1、查询的语句 : 根据用户id查询用户信息【因为引入了package标签】
     加typeAliases(类型别名) 不用写 包名+类名,直接写resultType="user" / "User" -->
    <select id="findUserById" resultType="user">
      select id,user_name ,birthday,sex,address from user where id= #{id}
    </select>


    <!--2、根据用户id查询用户姓名 -->
    <!-- 如果不写 resultType="string" 报错如下
    Cause: org.apache.ibatis.executor.ExecutorException:
    A query was run and no Result Maps were found for the Mapped Statement
    'cn.mybatis.demo.mapper.UserMapper.findNameByUserId'. -->
    <select id="findNameByUserId" resultType="string">
        select user_name from user where id=#{id}
    </select>

    <!--3、根据用户id查询用户生日-->
    <select id="findBirthdayById" resultType="Date">
      select birthday from user where id = #{id}
    </select>

    <!-- 4、向数据表中插入数据 -->
    <insert id="addUser">
        insert into user(id,user_name,birthday,sex,address)
        values
        (#{id},#{userName},#{birthday},#{sex},#{address})
    </insert>

    <!-- 5、根据用户id删除数据 -->
    <delete id="deleteUserById">
        delete from user where id = #{id}
    </delete>

</mapper>

<?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="org.example.UserMapper">
     <select id="方法名" resultType="pojo名称默认首字符小写/_基本类型/jdk对象类型小写">
     select * from user where id = #{id}
     </select>
</mapper>
==========================================================================


<select id="findBirthdayById" resultType="java.sql.Date">
 	select birthday from user where id = #{id}
</select>

<insert id="insertAuthor">
  insert into Author (id,username,password,email,bio)
  values (#{id},#{username},#{password},#{email},#{bio})
</insert>

<update id="updateAuthor">
  update Author set
    username = #{username},
    password = #{password},
    email = #{email},
    bio = #{bio}
  where id = #{id}
</update>

<delete id="deleteAuthor">
  delete from Author where id = #{id}
</delete>
1.select
格式:
   <select id="对应接口中的方法名称" resultType="pojo名称默认首字符小写/_基本类型/jdk对象类型小写">
      select * from t where id=#{任意变量}
   </select>
2.insert
格式:
   <insert id="xx">
      insert into t values(null,#{对应pojo中属性名称},....)
   </insert>
3.update
格式:
    <update id="xxx">
      update t set 字段1=#{对应pojo中属性名称},.... where id=#{id}
    </update>
4.delete
格式:
   <delete id="xxx">
     delete from t where id=#{id}
   </delete>
总结案例:根据给定的表结构,完成sql操作功能

表:Student

字段名数据类型说明
stu_idint学生id 主键,自动增长
stu_namevarchar(30)学生姓名
stu_ageint学生年龄
stu_genderchar(2)学生性别 默认值:男
stu_phonevarchar(20)手机号
stu_addressvarchar(50)地址

使用MyBatis技术,实现以下需求

需求1:向Student表中插入学生信息

需求2:获取学号为2的学生信息

需求3:修改’小明’的地址为"超级无敌大上海之XXX程序员"

需求4:如手机号’13800138000’存在,则删除该手机号学生信息

需求5:获取所有学生信息

测试类
package homework.main;

import com.itheima.mb.pojo.User;
import homework.mapper.StudentMapper;
import homework.pojo.Student;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import util.MybatisUtil;

import java.util.List;

public class StudentTest {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    StudentMapper stuMapper = sqlSession.getMapper(StudentMapper.class);

    // 1、向Student表中插入学生信息 [insert]
    @Test
    public void insertTest() {
        System.out.println("--------------1、insertStudent----------------");
        Student student = new Student(null, "景甜", 23,
                "女", "15211314520", "西安");
        int rowInsert = stuMapper.insertStudent(student);
        if (rowInsert > 0) {
            MybatisUtil.commit();  // 手动提交事务
            System.out.println("Insert Successful!");
        }
        MybatisUtil.close();
    }

    // 2、获取学号为2的学生信息 [select]
    // 5、 获取所有学生信息 [select]
    @Test
    public void selectTest() {
        System.out.println("--------------2、selectUserById----------------");
        Student student = stuMapper.selectStudentById(1);
        System.out.println(student);

        System.out.println("--------------5、selectUserById----------------");
        List<Student> list = stuMapper.selectStudentAll();
        // System.out.println(list);
        for (Student stu : list) {
            System.out.println(stu);
        }

        MybatisUtil.close();
    }

    // 3. 修改'小明'的地址为"上海市航头镇18号黑马程序员" [update]
    @Test
    public void updateTest() {
        System.out.println("--------------3、updateAddressByName----------------");
        int rowUpdate = stuMapper.updateAddressByName("上海市航头镇18号黑马程序", "小明");
        if (rowUpdate > 0) {
            MybatisUtil.commit();
            System.out.println("Update Successful!");
        }
        MybatisUtil.close();
    }

    // 4. 如手机号'13800138000'存在,则删除该手机号学生信息 [delete]
    @Test
    public void deleteTest() {
        System.out.println("--------------4、deleteUSerByPhone----------------");
        int rowDelete = stuMapper.deleteStudentByPhone("13800138000");
        if (rowDelete > 0) {
            MybatisUtil.commit();
            System.out.println("Delete Successful!");
        }

        MybatisUtil.close();
    }

}

StudentMapper映射接口
package homework.mapper;

import homework.pojo.Student;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface StudentMapper {

    // 1. 向Student表中插入学生信息 [insert]
    abstract  int insertStudent(Student student);

    // 2. 获取学号为2的学生信息 [select]
    abstract Student selectStudentById(int stuId);

    // 3. 修改'小明'的地址为"上海市航头镇18号黑马程序员" [update]
    abstract int updateAddressByName(@Param("stuAddress") String stuAddress, @Param("stuName") String stuName);

    // 4. 如手机号'13800138000'存在,则删除该手机号学生信息 [delete]
    abstract int deleteStudentByPhone(String stuPhone);

    // 5. 获取所有学生信息 [select]
    public abstract  List<Student> selectStudentAll();


}

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>
    <!-- 1、使用Properties标符,引入外部的资源文件 -->
    <properties resource="config/jdbc.properties"/>

    <!--2、mapUnderscoreToCamelCase 自动把带有下划线的数据库字段名转换为驼峰命名,
     赋值给相应的PoJo类属性且不区分大小写
     eg: user_name ==> userName/username   不写settings标签 属性得不到 数据库字段的值
     -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!-- 3、这里使用package标签 : 指定包下的所有类型,都具有别名-->
    <!--<typeAlias type="com.itheima.mb.pojo.User" alias="User"/>-->
    <!-- 需要给指定的类型,取别名。 如果有多个指定类型时,需要书写多个typeAlia  -->
    <!-- 使用package,指定包名。 指定包下的所有类型,都具有别名
    eg: com.pojo.User     com.pojo.Student
    指定包名:com.pojo    那么【 com.pojo.User 】的别名为: user 或  User
     -->
    <typeAliases>
        <package name="homework.pojo"/>
<!--        <package name="com.itheima.mb.pojo"/>-->
<!--        <package name="cn.mybatis.demo.pojo"/>-->
    </typeAliases>

    <!-- 4、environments指定开发环境dev / test-->
    <environments default="dev">
        <!-- 1. 开发数据库环境-->
        <environment id="dev">
            <!-- 1.2 事务管理器: 数据库中的事务由JDBC完成 -->
            <transactionManager type="JDBC"/>
            <!-- 1.3 type="POOLED" 表示: 使用mybaits默认的连接池 -->
            <dataSource type="POOLED">
                <!-- 1.4 给(属性)参数赋值 :${key} 获取到jdbc.properties属性集配置文件参数Value值 -->
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.userName}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <!--2. 测试数据库环境-->
        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClass}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.userName}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

    </environments>

    <!-- 5、这里使用package标签:把指定接口下所有的接口引入  适用于:注解方式 -->
    <!-- 把指定接口下所有的接口引入
    加载接口,关联映射文件方式package(掌握)
    <package name="com.itheima.mb.mapper"/>   接口的包路径
    前提条件:
    1、接口名和映射文件名保持一致;
    2、路径保持一致;
    3、映射文件中的namespace必须与接口的全限定名称一致;
    适用于:注解方式 -->
    <!-- 引入映射文件    适用于:  xml方式
    <mapper resource="com/itheima/mb/mapper/UserMapper.xml"/>  文件的相关路径
    <mapper class="com.itheima.mb.mapper.UserMapper" />  接口实现类的完全限定类名
    -->
    <mappers>
        <!--        <package name="com.itheima.mb.mapper"/>-->
        <!-- 1、ithima UserMapper映射文件的相关路径-->
        <!-- <mapper resource="com/itheima/mb/mapper/UserMapper.xml"/> -->
<!--        <package name="com.itheima.mb.mapper"/>-->

        <!-- 2、mybatis UserMapper映射文件的相关路径-->
<!--        <package name="cn.mybatis.demo.mapper"/>-->

        <!-- 3、homework StudentMapper映射文件的相关路径-->
        <package name="homework.mapper"/>

    </mappers>

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

<!--  命名空间为 映射文件的相对src目录的路径  【加载映射接口,使用其中抽象方法】 -->
<mapper namespace="homework.mapper.StudentMapper">

    <!-- 1、向Student表中插入学生信息  加了typeAliases(类型别名) [insert]
     【引入了package标签】所以不用写 包名+类名,直接写resultType="user" / "User" -->
    <insert id="insertStudent">
        insert into student(stu_id,stu_name,stu_age,stu_gender,stu_phone,stu_address)
        values(#{stuId},#{stuName},#{stuAge},#{stuGender},#{stuPhone},#{stuAddress})
    </insert>


    <!--2、获取学号为2的学生信息 [select]-->
    <select id="selectStudentById" resultType="Student">
        select * from student where stu_id = #{stuId}
    </select>

    <!--3、修改'小明'的地址为"上海市航头镇18号黑马程序员"[update]-->
    <update id="updateAddressByName" >
      update student set stu_address = #{stuAddress} where stu_name = #{stuName}
    </update>

    <!-- 4、如手机号'13800138000'存在,则删除该手机号学生信息 [delete] -->
    <delete id="deleteStudentByPhone">
        delete from student where stu_phone = #{stuPhone}
    </delete>


    <!-- 5、获取所有学生信息 [select] -->
    <select id="selectStudentAll"  resultType="Student">
        select * from student
    </select>



</mapper>

三、Mybatis映射文件配置

1、参数

1)参数值的传入
01)parameterType(了解)

CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型。

入参数据有以下几种类型:

HashMap,基本数据类型(包装类),实体类;

设置传入这条语句的`参数类的完全限定名或别名。 

这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数类型。
`在mybatis中入参的数据类型分为2大类:
1. 基本数据类型:int,string,long,Date等;
2. 复杂数据类型:类(pojo)和Map;
说明:如果传递参数是数组或者集合,底层都会封装到Map集合中。
【基本类型数据】 
<!--根据id查询--> 
<!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名.MyBatis 可以 通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推 断出具体传入语句的参数类型。--> 
<select id="findById" resultType="user" parameterType="int"> select * from user where id = #{id} 
</select> 

【pojo类型】 
底层原理: User类:user_name属性,自动生成setter或者getter方法时,getUserName 当我们向xml传入#{user_Name}---->getUser_Name--->利用反射根据方法名称获取方法对象--- >报反射异常 综上: 1)传入的时pojo的话,传入xml的变量名称与pojo类下属性名称要一致; 2)pojo中属性名称定义要尽量遵循驼峰命名,或者在自动生成getter或者setter方法时,自己手动纠 正;
<insert id="savetUser" parameterType="User">
    INSERT INTO user(...) values(#{userName},...);
</insert> 

说明:

对于parameterType属性可以不书写,

那么MyBatis 就会通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。

02)自增主键回填(了解)

新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值

直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库。

如:MySQL、SQL Server

`使用insert标签的子标签selectKey+last_insert_id()函数实现实现自增主键回填

keyColumn 		主键在表中对应的列名
keyProperty 	主键在实体类中对应的属性名
resultType 		主键的数据类型
order BEFORE 	会首先选择主键,设置 keyProperty 然后执行插入语句 AFTER: 在添加语句后执 行查询主键的语句

案例演示:

接口:

/**
 * 添加用户
* 返回值是影响的行数
* @param user
* @return
 */
Integer addUserAndGetFkId(User user);

映射文件:

<!--
 selectKey:表示查询主键字段的标签 keyColumn:表示表中字段名称,一般指主键
名称
 keyProperty="id":表示pojo类中对应主键的属性名称
 order="AFTER":表示在操作之前或者之后获取主键值
 -->
----------------------------------------------------------------------
1、自增主键回填: selectKey标签 + last_insert_id()函数
<insert id="addUserAndGetFkId">
 	insert into user values(null,#{username},#{birthday},#{sex},#{address})
    
	<selectKey keyColumn="id" keyProperty="id" resultType="int" 
	order="AFTER">
 		select last_insert_id()
 	</selectKey>
 </insert>
----------------------------------------------------------------------
2、自增主键回填: insert标签的属性useGeneratedKeys,keyProperty,keyColumn
<insert id="addUserAndGetFkId2" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
 	insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert>
----------------------------------------------------------------------

测试:

@Test
public void work01() {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    System.out.println("----------添加姓名为张三的用户----------------");
    User user = new User();
    user.setuName("王五");
    Integer row1 = mapper.addUserAndGetFkId(user); 
    System.out.println("影响行:"+row1); // 1
    // 插入完成之后,查询的主键值,会封装到User对象中的id属性中
    Integer uId = user.getuId();
    System.out.println("主键值" + uId);
}
03)Mybatis入参个数是单参和多参(掌握)
单个参数
【映射接口传参】
User queryById(Integer id); //接口方法传入一个参数

【映射文件接收参数】
<!--根据id查询-->
<select id="queryById" resultType="User" parameterType="int">
	select * from user where id = #{id}
</select>
    
// 在xml中通过#{任意变量名}可以接收到参数

接口传入时是单个参数,可在xml中使用任意变量取接收但是不建议乱写,最好见名知意。

参类型是Pojo和Map的多参

三种方案解决入参是多个参数的问题

方式1:使用参数索引获取:arg0,arg1 (了解即可) 
<select id="findUsersByUserNameAndSex" resultType="user">
 	select * from user where user_name=#{arg0} and sex=#{arg1}
</select>
 
    
方式2:使用参数位置获取:param1,param2 (了解即可)
<select id="findUsersByUserNameAndSex" resultType="user">
	select * from user where user_name=#{param1} and sex=#{param2}
</select>
    
    
方式3:使用命名参数获取,在接口处明确指定传入参数的名称 (掌握)
/**
 * 需求:根据用户名和性别查询用户
* @param name
* @param sex
* @return
 */
第一步:接口处明确指定传入参数的名称
List<User> findUsersByUserNameAndSex(@Param("name") String name,@Param("sex") String sex);
第二步:在接收参数时,通过指定的名称获取参数值
<select id="findUsersByUserNameAndSex" resultType="user">
	select * from user where user_name=#{name} and sex=#{sex}
</select>
2)参数值的获取

获取参数的方式

在mybatis中获取传入参数值的方式有两种:#{}${}

1、#{}取值

使用#{}的sql是进行预编译的,可以防止sql注入

2、${}取值

• ${id} 获取id值时,必须使用命名参数取值@param

• 如果是取单个值,也可使用${value}获取

• 参数值直接拼接到sql中,会有sql注入的风险

#{}:sql进行预编译处理,防止sql注入
${}:参数与sql直接拼接,有sql注入的风险
01)${}取值的应用场景
获取的是pojo类型参数

接口方法传入pojo类型的数据时,
xml中使用#{pojo属性名称}可直接获取传入对象对应的属性值

1、【接口】
/**
 * 插入功能
* @param user
*/
void saveUser(User user);

2、【映射文件】
<insert id="saveUser">
 insert into user values(null,#{username},#{age},#{birthday},#{sex},#{address})
</insert>
入参是Map类型

接口方法传入Map类型的数据时,

xml中使用#{map中key}可直接获取map中的value值

1、【接口:】
User queryByNameAndSex(Map map);

2、【映射文件:<select id=“queryByNameAndSex" resultType="user" parameterType="map"> 		select * from user where user_name=#{name} and sex=#{sex}
</select>
02)${}取值的应用场景

在一些特殊的应用场景中,需要对SQL语句部分(不是参数)进行拼接

这个时候就必须使用${}来进行拼接,不能使用#{}

例如:

1、企业开发中随着数据量的增大,往往会将数据表按照年份进行分表,如:2017_user,2018_user....,对这些表进行查询就需要动态把年份传入进来,而年份是表名的一部分,并不是参数,JDBC无法对其预编译,所以只能使用${}进行拼接:
SELECT * FROM ${year}_user;
    
2、根据表名查询数据总记录数:
SELECT COUNT(*) FROM user
SELECT COUNT(*) FROM order
SELECT COUNT(*) FROM ${tableName}

结论:如果需要设置到SQL中的不是查询的条件,只能使用${}拼接

**案例演示:**根据输入的表名统计指定表下的总记录数

在这里插入图片描述

2、结果映射

1)resultType 结果映射
简单的结果映射

 在使用原生的JDBC操作时,对于结果集ResultSet,需要手动处理;

 mybatis框架提供了resultType和resultMap来对结果集进行封装;

 只要一个方法有返回值需要处理,那么 resultType和resultMap必须有一个;

从sql语句中返回的期望类型的类的完全限定名或别名

注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身

可以使用 resultType 或 resultMap,但不能同时使用

`① 返回值是基本类型
例如:
int ,string ===>resultType="写入对应的基本类型别名或者全限定名即可" 
1)基本类型 int short double ...  别名: _基本类型名称
2)包装类 类 String ArrayList ....  别名: 类名首字母小写
3)自定义类 扫包取别名 类首字母小写(大写也可以)

`② 返回值为一个pojo(User)对象时
1)表中列明要与pojo中属性名称一致
2)若表中列名与pojo中属性名称不一致,可使用as取别名使其一致即可
3)如果满足驼峰映射,也可以开启驼峰映射设置

`③返回值为一个List<User>时
当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型
Map的结果形式
  1. map接收单条记录

map中的key就是查询的表的字段名称,如果使用as区别名,

那么可以就是对应的别名的名称;

  1. map接收多条记录

  2. 需要指定作为key的字段,一般是主键字段 @MapKey(“指定字段名称”)

  3. 指定每一条记录封装的对象类型;

代码演示1 ,返回一条数据

查询id是1的数据,
将查询的结果封装到Map<String,Object>中返回一条数据,封装到map中

1) 定义接口
/**
 * 需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中
* @param id
* @return
 */
Map<String,Object> findMapById(@Param("id") Integer id);

2) 定义映射文件配置
<select id="findMapById" resultType="map">
	select id,user_name as userName,address from user where id=#{id}
</select>
    
3) 结果
{address="xxxx",id="xxx",userName="xxx"}

代码演示2 ,返回多条数据

查询数据表所有的数据封装到Map<Integer,User>集合中,

Key值为一条记录的主键,Value值为pojo的对象
在这里插入图片描述

2)resultMap结果映射

1、正常开发中,数据库字段名称与Pojo类属性名称不一致时,一般通过驼峰映射或者AS关键字取别名可以搞定,但是很多场景下,对于复杂的orm映射,上述的2种方式就不能适用了;

2、ResultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决复杂映射问题:

  1. POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式,比如id和userId)
  2. 完成高级查询,比如说,一对一、一对多、多对多。
resultMap完成结果集的封装步骤(resultSet -->JavaBean)

自定义配置实体类属性和表字段映射关系的步骤如下:

1、 配置自定义结果集

2、 配置id映射

3、 配置其他普通属性的映射

**步骤一:**将驼峰匹配注释掉

一旦注释掉驼峰匹配,那么再通过findById查询的结果中,用户名就无法封装了,此时我们可以尝试使用ResultMap来解决这个问题

<settings> 
    <!--作用:表:user_name 类:userName/username 自动映射-->
    <setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>

**步骤二:**配置resultMap

resultMap标签的作用:自定义结果集,自行设置结果集的封装方式

id属性:resultMap标签的唯一标识,不能重复,一般是用来被引用的
type属性:结果集的封装类型 
autoMapping属性:操作单表时,不配置默认为true,如果pojo对象中的属性名称和表中字段名称相同,则自动映射
<!--type="user" 表示结果集的封装类型是user--> 
<resultMap id="userResultMap" type="user" autoMapping="true">
    <!--配置主键映射关系-->
 	<id column="id" property="id"></id> 
    <!--配置用户名的映射关系 column 表示数据表列 property表示pojo的属性--> 		<result column="user_name" property="name"></result> 
</resultMap>

代码演示

使用resultMap完成根据用户id查询用户信息的结果集的封装(resultSet  JavaBean)

在这里插入图片描述

小结

1、resultMap标签映射流程解析:

在这里插入图片描述

2、resultMap标签的作用?

1)提高了代码的复用性;
2)结果集映射的;(将查询的结果映射到pojo类下)

3、resultMap有哪些属性和子标签?

<resultMap id="唯一标识" type="映射的pojo类" autoMapping="true">
	<!--主键字段映射-->
	<id column="表中主键字段" property="pojo类中主键对应的属性名称"/>
	<!--普通字段映射-->
	<result column="表中非主键字段" property="pojo类中非主键对应的属性名称"/>
</resultMap>

3、独立文件中的SQL片段

sql标签可以定义一个sql片段,在需要使用该sql片段的地方
通过<include refid="sql片段id"/>标签来使用

【1】定义SQL片段
<sql id="userCommonSql">
 id,user_name,birthday,sex,address
</sql>
注意:SQL片段必须设置id属性;

【2】使用SQL片段
在SQL语句中通过`<include>`标签引入SQL片段
<select id="findById" resultMap="userMap">
	select <include refid="userCommonSql"/> from user where id=#{id}
</select

sql标签可以定义一个sql片段,在需要使用该sql片段的地方,

通过<includerefid=“sql片段id”/>标签来使用。很多时候同一个sql片段,可能在多个映射文件中都有使用,如果每一个映射文件都编写一个相同的sql就比较麻烦,

因此可以将通用的sql片段都定义在一个专门存放sql片段的映射文件中,然后由其他映射文件引用它即可。

如下,在src目录下新增CommonSQL.xml文件

【1】 新建SQL片段文件【CommonSQL.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="CommonSql">
 <sql id="userSql">
 id,user_name,birthday,sex,address
 </sql>
 <sql id="empSql">
 eid,ename,age
 </sql>
</mapper>

【2】在mybatis核心配置文件mybatis‐config.xml引入SQL片段文件。
定义好sql片段的映射文件之后,接下来就该使用它了,首先应该把该映射文件引入到mybatis的全局配置文件中(mybatis‐config.xml):
<mappers>
     <mapper resource="CommonSql.xml"/>
     ……
</mappers> 

【3】引用SQL片段
最后在需要使用该sql片段的地方通过include标签的refId属性引用该sql片段:
<include refId=”名称空间.sql片段的id” />在UserMapper.xml的映射文件中,
进一步改造根据用户名查询用户信息
<select id="findById" resultMap="userMap" parameterType="int">
 	select <include refid="CommonSql.userSql"/> from user where id = ${id}
</select>

四、动态SQL

MyBatis 的强大特性之一便是它的动态SQL。可以根据不同条件拼接 SQL 语句

在 MyBatis 之前的版本中,有很多元素需要花时间了解。

MyBatis 3 开始精简了元素种类,现在只需学习原来一半的元素便可。

MyBatis 采用功能强大的 OGNL 的表达式淘汰其它大部分元素。

OGNL( Object Graph Navigation Language )对象图导航语言,这是一种强大的表达式语言, 通过它可以非常方便的来操作对象属性

动态SQL中的业务逻辑判断需要使用到以下运算符:

1、ognl表达式
1. e1 or e2 满足一个即可 
2. e1 and e2 都得满足 
3. e1 == e2,e1 eq e2 判断是否相等 
4. e1 != e2,e1 neq e2 不相等 
5. e1 lt e2:小于 lt表示less than 
6. e1 lte e2:小于等于,其他gt(大于),gte(大于等于) gt 表示greater than 
7. e1 in e2 
8. e1 not in e2 
9. e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2 
10. !e,not e:非,求反 
11. e.method(args)调用对象方法 
12. e.property对象属性值 user.userName 
13. e1[ e2 ]按索引取值,List,数组和Map 
14. @class@method(args)调用类的静态方法 
15. @class@field调用类的静态字段值
2、Mybatis常见标签如下:
If标签:判断
<if test="判断条件">
	满足条件sql加入拼接
</if>
 
说明:
 1)if标签:判断语句,用于进行逻辑判断的。如果判断条件为true,则执行if标签的文本内容
 2)test属性:用来编写表达式,支持ognl
/**
 * 【需求】:查询男性用户,如果输入了用户名,
* 按用户名模糊查询,如果没有输入用户名,就查询所有男性用
*/
List<User> findUsersByUserName(@Param("userName") String userName);
<select id="findUsersByUserName" resultMap="userMap">
     select * from user where sex='男'
     <if test="userName!=null">
     	and user_name like concat('%',#{userName},'%')
     </if>
</select>
@Test
public void test22(){
UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
    //select * from user where sex='男'
    //List<User> users = mapper.findUsersByUserName(null);
    //select * from user where sex='男' and concat('%',?,'%');
    List<User> users = mapper.findUsersByUserName("唐僧");
    System.out.println(users);
    MybatisUtil.close();
}
choose 标签:分支判断
choose标签:(分支选择,多选一,遇到成立的条件即停止)
	when子标签:(编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行)
        test属性:(编写ognl表达式)
otherwise子标签:(当所有条件都不满足时,才会执行该条件。)
<select id="findActiveBlogLike" resultType="Blog">
     SELECT * FROM BLOG WHERE state = ‘ACTIVE’
     <choose>
         <when test="title != null">
         AND title like #{title}
         </when>
         <when test="author != null and author.name != null">
         AND author_name like #{author.name}
         </when>
         <otherwise>
         AND featured = 1
         </otherwise>
     </choose>
</select>
where标签
where标签:拼接多条件查询时
     1、能够添加where关键字;
     2、能够去除多余的and或者or关键字;
     
where多条件语法格式:
<!--举例-->
<select>
     select * from t
     <where>
         <if test="条件">
        字段 = #{值1}
         </if>
         <if test="条件">
         AND 字段名 = #{值2}
         </if>
     </where>
</select>

<!--
 where标签作用:
 1)适当添加where关键字
 2)被where标签包裹的sql会自动去除多余的and或者or关键字
-->
set标签
set标签:
在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。

语法格式:
<update>
 update t
     <set>
         <if test='条件'>
        字段名1=值1,
         </if>
         <if test='条件2'>
        字段名2=值2,
         </if>
     </set>
     where 条件
</update>

在这里插入图片描述

foreach:循环遍历标签
foreach标签作用:遍历集合或者数组
语法格式:
<foreach collection=“集合名或者数组名“  item=”集合中每一个元素" separator="标签分隔符" 
open="以什么开始" close="以什么结束">
                          
   #{元素}
                          
</foreach>
                          
说明:
collection属性:接收的集合或者数组,集合名或者数组名
item属性:集合或者数组参数中的每一个元素
separator属性:标签分隔符
open属性:以什么开始
close属性:以什么结束

在这里插入图片描述

mybatis延迟加载【了解】

1、延迟加载概述

  1. 应用场景

    ​ 如果查询订单并且关联查询用户信息。如果先查询订单信息即可满足要求,当我们需要查询用户信息时再查询用户信息。把对用户信息的按需去查询就是延迟加载。

  2. 延迟加载的好处

    先从单表查询、需要时再从关联表去关联查询,大大提高 数据库性能,因为查询单表要比关联查询多张表速度要快。

  3. 延迟加载的条件:

    1)resultMap可以实现高级映射(使用association、collection实现一对一及一对多映射),association、collection具备延迟加载功能。

    2)延迟加载是针对分步查询而言的

2、开启延迟加载

Mybatis的延迟加载功能默认是关闭的

需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能

需要在mybatis-config.xml全局配置文件中通过setting标签配置来开启延迟加载功能开启延迟加载的属性:

lazyLoadingEnabled:全局性设置懒加载。默认为false,true表示开启延迟加载
aggressiveLazyLoading:false表示关闭积极加载
说明:这两个属性必须一起设置

【示例】

<settings>
     <!--开启延迟加载-->
     <setting name="lazyLoadingEnabled" value="true"/>
     <!--关闭积极加载-->
     <setting name="aggressiveLazyLoading" value="false"/>
 </settings>

3、延迟加载测试

延迟加载需求:通过订单编号20140921003查询order并延迟加载user。就是演示上述演示过的一对一的表关系案例。

分析:

如果改成延迟加载,也就意味着,先查询order,等需要的时候再去查询user,那就相当于将上面的一条语句变成了两条语句:

1、通过订单编号查询order

2、通过查询出来的order中的user_id查询user

SQL语句:

分步查询:
	#第一步:根据order_number查询订单信息;
	SELECT * FROM tb_order WHERE order_number = '20140921003';
	#第二步:根据订单信息中的user_id查询出下单人的信息;
	SELECT * FROM tb_user WHERE id = 1;
第一步:编写接口方法及映射文件

1.在OrderMapper接口中新建:queryOrderUserLazy方法;

    Order queryOrderUserLazy(String orderNumber);

2.编写映射文件,分部查询,通过association标签下的column和select属性获取参数并传递到子查询中;

    <!--引入在查询order信息的同事需要将用户的信息一并封装到order实体下,需要进行高级映射-->
    <resultMap id="orderUserLazyMap" type="order" autoMapping="true">
        <!--主键映射-->
        <id column="order_id" property="id"/>
         <!--普通字段映射 order_number:orderNumber, 因为settting中设置了驼峰规则,所以可省略-->
        <result column="order_number" property="orderNumber"/>
        <!--用于映射user对象,关系:一对一,可使用association实现映射-->
        <association property="user" javaType="user"  column="user_id" select="com.heima.dao.UserMapper.queryById">
        </association>
    </resultMap>

    <select id="queryOrderUserLazy" resultMap="orderUserLazyMap">
           select * from tb_order where order_number=#{orderNumber}
    </select>
第二步:开启懒加载

在mybatis-config.xml全局配置文件中,开启懒加载;

<settings>
     <!--开启延迟加载-->
     <setting name="lazyLoadingEnabled" value="true"/>
     <!--关闭积极加载-->
     <setting name="aggressiveLazyLoading" value="false"/>
 </settings>
第三步:测试
    @Test
    public void queryOrderUserLazy(){
        Order order = orderMapper.queryOrderUserLazy("20140921001");
        System.out.println("此时没有进行用户信息查询");
        System.out.println(order.getOrderNumber());
        //当我们使用user对象时,才从数据库中加载
        User user = order.getUser();
        System.out.println(user.toString());
    }
小结:

延迟加载的利与弊
好处:先从单表进行查询数据,需要时再从关联表去关联查询,将会提高数据库性能,因为查询单表要比关联查询多张表速度快。

坏处: 因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

五、Mybatis高级查询【重点】

1、mybatis高级查询环境准备

在这里插入图片描述

配置UserMapper.xml映射文件和接口

1. 定义接口
public interface UserMapper {
	User findByUserId(@Param("id") Long id);
}

2. 配置映射文件
<?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.heima.mapper.UserMapper">
     <select id="findByUserId" resultType="user">
        select * from tb_user where id=#{id}
     </select>
</mapper>
         
         
单元测试
public class TestAll2 {
    @Test
    public void test1(){
        UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
        User user = mapper.findByUserId(1l);
        System.out.println(user);
        MybatisUtil.close();
     }
}
  

2、一对一查询

语法格式: 
<resultMap id="映射ID" type="主表实体名称" autoMapping="true" >
<!-- 添加主表语主表实体映射 -->
......
<!--association:配置关联对象(User)的映射关系,一般与resultMap标签联合使用 -->
    <association property="主表实体中对应从表的属性名称" javaType="从表实体类型" autoMapping="true">
    <!-- 添加从表中字段与实体属性映射关系 -->
    </association>
</resultMap>

案例演示:

通过订单编号20140921003查询出订单信息,并查询出下单人信息

-- 方式1:分步查询
-- 1.1根据订单编号查询订单信息
select * from tb_order where order_number='20140921003';-- user_id:1
-- 1.2根据user_id=1查询下单人信息
select * from tb_user where id=1;
-- 方式2:关联查询
select tb_order.id as order_id,tb_order.order_number,tb_user.* 
from tb_order,tb_user 
where tb_user.id=tb_order.user_id 
and tb_order.order_number='20140921003';

添加order接口及方法

public class Order {
private Integer id;
private String orderNumber;
private User orderUser;
//getter setter toString
}

创建order映射文件,编写SQL

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 
<mapper namespace="com.heima.mapper.OrderMapper">
    ---------------------------------------------------------------------
        <!--自定义映射规则--> 
        <resultMap id="orderMap" type="Order" autoMapping="true"> 
            <id column="order_id" property="id"/> 
            <result column="order_number" property="orderNumber"/>
            <!-- association:表示一对一映射 property="orderUser":表示指定映射的属性名称 javaType="User":指定映射属性的类型 autoMapping="true":开启自动映射(查询字段名称与pojo类中属性名称一致,可以不写) -->
            <association property="orderUser" javaType="User" autoMapping="true"> 
                <id column="id" property="id"/> 
                <result column="user_name" property="userName"/> 					</association> 
        </resultMap> 
	---------------------------------------------------------------------
        <select id="findOrderByOrderNumber" resultMap="orderMap"> select tb_order.id as order_id, tb_order.order_number, tb_user.* from tb_order, tb_user where tb_user.id = tb_order.user_id and tb_order.order_number = #{orderNumber} 
        </select> 
    
</mapper>

测试

@Test
public void test2(){
    OrderMapper mapper = MybatisUtil.getMapper(OrderMapper.class);
    Order order = mapper.findOrderByOrderNumber("20140921003");
    System.out.println(order);
    MybatisUtil.close();
}

在这里插入图片描述

3、一对多查询

案例:查询id为1的用户及其订单信息

-- 方式1:分步查询
-- 1.1 根据id查询用户信息
select * from tb_user where id=1; -- id=1
-- 1.2 根据用户id查询订单集合
select * from tb_order where user_id=1;
-- 方式2:一步查询
select
tb_user.*,
 tb_order.id as order_id,
 tb_order.order_number
from tb_user , tb_order
where tb_user.id = tb_order.user_id
and tb_user.id = 1;

User实体添加映射关系

public class User implements Serializable{
    
private List<Order> orders;
    
private Long id;
private String userName; // 用户名
private String password; // 密码
private String name; // 姓名
private Integer age; // 年龄
private Integer sex; //0-女 1-男
// getter and setter and toString
}

编写接口

/**
 * 根据用户id查询用户信息,包含订单集合信息
* @param id
* @return
 */
User findUserAndOrdersByUserId(@Param("id") Long id);

编写sql映射文件关联订单集合

<!--定义一对多的映射规则-->
	<resultMap id="userMap" type="user" autoMapping="true">
        <!--映射主表-->
        <id column="id" property="id"/>
        <result column="user_name" property="userName"/>
   		<!--配置一对多映射-->
        <collection property="orders" javaType="list" ofType="order" autoMapping="true">
         <id column="order_id" property="id"/>
         <result column="order_number" property="orderNumber"/>
         </collection>
    </resultMap> 

<select id="findUserAndOrdersByUserId" resultMap="userMap">
     select
     tb_user.*,
     tb_order.id as order_id,
     tb_order.order_number
     from tb_user,tb_order
     where tb_user.id = tb_order.user_id
     and tb_user.id = #{id}
</select>

测试

@Test
public void test3(){
    UserMapper mapper = MybatisUtil.getMapper(UserMapper.class);
    User user = mapper.findUserAndOrdersByUserId(1l);
    System.out.println(user);
    MybatisUtil.close();
}

在这里插入图片描述

4、多对多查询

案例:查询订单号为20140921001的订单的详情信息,包含订单信息以及订单中的商品信息

第1步:分析SQL实现思路

select
tor.id as order_id,
tor.order_number,
tord.id as detail_id,
tord.status,
tord.total_price,
ti.id as item_id,
ti.item_detail,
ti.item_name,
ti.item_price
from tb_order as tor,
 tb_orderdetail as tord,
 tb_item ti
where tor.id = tord.order_id
and ti.id = tord.item_id
and tor.order_number = '20140921001';

第2步:添加类属性关联关系

public class Order {
private Integer id;
private String orderNumber;
private User user;
private List<Orderdetail> orderdetailList;
 ...
}

一条订单详情记录中都包含了一条商品信息,需要在Orderdetail中添加一个Item属性

public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
private Item item;
 ......
}

第3步:编写接口方法

/**
 * 根据订单编号查询订单信息和详情信息以及关联的商品信息
* @param orderNumber
* @return
 */
Order findByOrderNumber2(@Param("orderNumber") String orderNumber);

第4步:编写SQL映射文件

<!--定义多对多映射规则-->
<resultMap id="orderMap2" type="order" autoMapping="true">
     <id column="order_id" property="id"/>
     <result column="order_number" property="orderNumber"/>
     <collection property="orderdetailList" javaType="list" 
    ofType="orderdetail" autoMapping="true">
         <id column="detail_id" property="id"/>
         <result column="total_price" property="totalPrice"/>
         
         <association property="item" javaType="item" autoMapping="true">
         <id column="id" property="id"/>
         <result column="item_name" property="itemName"/>
         </association>
     </collection>
</resultMap>

<select id="findByOrderNumber2" resultMap="orderMap2">
  	..........  
</select>

第5步:测试

@Test
public void test4(){
     OrderMapper mapper = MybatisUtils.getMapper(OrderMapper.class);
     Order order = mapper.findByOrderNumber2("20140921001");
    System.out.println(order);
     MybatisUtils.close();
}
综合案例:

查询订单号为20140921001的订单信息、订单所属用户信息、订单中的详细商品信息:

映射文件

<!--定义多对多映射规则-->
<resultMap id="orderMap3" type="order" autoMapping="true">
     <id column="order_id" property="id"/>
     <result column="order_number" property="orderNumber"/>
    
     <association property="user" javaType="user" autoMapping="true">
     <id column="user_id" property="id"/>
     <result column="user_name" property="userName"/>
     </association>
    
     <collection property="orderdetailList" javaType="list" ofType="orderdetail" 
    autoMapping="true">
         <id column="detail_id" property="id"/>
         <result column="total_price" property="totalPrice"/>
         
         <association property="item" javaType="item" autoMapping="true">
         <id column="id" property="id"/>
         <result column="item_name" property="itemName"/>
         </association>
     </collection>
</resultMap>

<select id="findByOrderNumber3" resultMap="orderMap3">
 SELECT
 tb_user.id as user_id,
 tb_user.age,
 tb_user.`password`,
 tb_user.sex,
 tb_user.user_name,
 tb_user.`name`,
 tb_order.id AS order_id,
 tb_order.order_number,
 tb_orderdetail.id AS detail_id,
 tb_orderdetail.total_price,
 tb_orderdetail.`status`,
 tb_item.*
 FROM
 tb_order,
 tb_orderdetail,
 tb_item,
 tb_user
 WHERE
 tb_order.id = tb_orderdetail.order_id
 AND tb_orderdetail.item_id = tb_item.id
 AND tb_order.user_id=tb_user.id
 AND tb_order.order_number = #{orderNumber}
</select>

测试

@Testpublic void test5(){
     OrderMapper mapper = MybatisUtils.getMapper(OrderMapper.class);
     Order order = mapper.findByOrderNumber3("20140921001");
    System.out.println(order);
     MybatisUtils.close();
}
小结
一对一关联查询实现步骤: 
1) 维护pojo对象之间一对一的关系:
           Order 包含属性:User类型的变量

2) 配置一对一的映射关系:
<resultMap id="唯一标识" type="映射的类" autoMapping="true">
    <id column="主键字段" property="映射类中属性名称"/>
    <result column="非主键字段" property="映射类中属性名称"/>
    <!‐‐一对一映射‐‐>
    <association property="映射的类中属性名称" javaType="java类型" autoMapping="true">
            <id column="主键字段" property="java类型中属性名称"/>
            <result column="非主键字段" property="java类型中属性名称"/>
    </association>
</resultMap>


一对多关联查询实现步骤:
1. 在一对多的场景中,一般主表中通过创建一个集合属性来包含从表的数据
如: user类包含List<Order> orders);
    
2. 如何使用collection标签建立对集合的关联映射?
<!--一对多映射-->
<resultMap id="唯一标识" type="映射的类" autoMapping="true">
     <id column="主键字段" property="映射类中属性名称"/>
     <result column="非主键字段" property="映射类中属性名称"/>
    <!--一对一映射-->
    <collection property="映射的类中属性名称" javaType="list" ofType="集合泛型" autoMapping="true">
         <id column="主键字段" property="java类型中属性名称"/>
         <result column="非主键字段" property="java类型中属性名称"/>
     </collection>
</resultMap>

5、ResultMap继承extend

如果两个ResultMap结果集有重叠的部分,可以通过extend属性继承简化;

将上述扩展通过extends继承后简化映射文件配置:

<resultMap id="orderMap4" type="order" extends="orderMap2" autoMapping="true">
     <association property="user" javaType="user" autoMapping="true">
     <id column="user_id" property="id"/>
     <result column="user_name" property="userName"/>
     </association>
</resultMap>

注意:特殊字符处理

编写Mapper映射文件时,有时候需要使用到一些诸如: > , < 之类的特殊字符。

这些字符不能直接书写在xml文件中,需要我们对其处理。

处理方式:使用转义字符代替特殊字符。

转义字符sql符号说明
&lt;<小于
&gt;>大于
&amp;&
&apos;单引号
&qouot;"双引号

举例:批量将id小于3的用户性别改为男,在映射文件中直接写<号,xml约束会报错!

<select id="findUsersLt" resultMap="userMap"> 
    select * from user where id &lt; #{id} 
</select>
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值