mybatis学习

mybatis框架介绍

mybatis是一款优秀的持久层框架,它支持定制化的SQL,存储过程以及高级映射(多表)。mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。它对JDBC的操作数据库的过程进行封装,使开发者只关注SQL本身,而不需要花费精力去处理例如注册驱动,创建connection,创建statement,手动设置参数,结果集检索等JDBC繁杂的过程代码。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

mybatis的优点

  1. 简单易学:mybatis本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个SQL映射文件即可。
  2. 使用灵活:Mybatis不会对应用程序或者数据库的现有设计强加任何影响。SQL语句写在XML里,便于统一管理和优化。
  3. 解除SQL与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易进行单元测试。SQL语句和代码的分离,提高了可维护性。

mybatis的缺点

  1. 编写SQL语句时工作量很大,尤其是字段多、关联表多时,更是如此。
  2. SQL语句依赖于数据库,导致数据库移植性差,不能更换数据库。
  3. 框架还是比较简陋,功能尚有缺失。

mybaits框架整体架构

在这里插入图片描述

1.配置文件:
	全局配置文件(核心配置文件):mybatis-confing.xml,作用:配置数据源(配置数据库连接信息),引入映射文件:XXXMapper.xml,作用:配置sql语句,参数,结果集封装类型等。
2.sqlsessionFactory:
	作用:获取sqlsession
	通过new SqlsessionFactoryBulider().build(inputStream)来构建,inputStream:读取配置文件的io流。
3.sqlsession:
	作用:执行CRUD操作
4.Executor:
	执行器,sqlsession通过调用它来完成具体的CRUD操作
5.mapper statement
	在映射文件里面配置,包含3部分内容:
	具体sql,sql执行所需要的参数类型,sql执行结果的封装类型
	参数类型和结果集封装类型包括3种:
	hashmap,基本数据类型和pojo

Mybatis的ORM方式

Object Relational Mapping 对象关系映射

在这里插入图片描述

mybatis的两种映射方式:

1.通过xml映射
2.通过注解

mybatis框架入门开发:

利用mybatis框架,从MySQL中查询所有的用户

在这里插入图片描述

数据库准备
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;
创建maven工程,导入坐标
  		<!--mybatis核心包-->
           <dependency>
               <groupId>org.mybatis</groupId>
               <artifactId>mybatis</artifactId>
               <version>3.5.0</version>
           </dependency>
          <!--logback日志包-->
           <dependency>
               <groupId>org.slf4j</groupId>
               <artifactId>slf4j-api</artifactId>
               <version>1.7.26</version>
           </dependency>
           <dependency>
               <groupId>ch.qos.logback</groupId>
               <artifactId>logback-core</artifactId>
               <version>1.2.3</version>
           </dependency>
           <dependency>
               <groupId>ch.qos.logback</groupId>
               <artifactId>logback-classic</artifactId>
               <version>1.2.3</version>
           </dependency>
           <!--mysql驱动-->
           <dependency>
               <groupId>mysql</groupId>
               <artifactId>mysql-connector-java</artifactId>
               <version>5.1.18</version>
           </dependency>
   
    		<dependency>
               <groupId>junit</groupId>
               <artifactId>junit</artifactId>
               <version>4.10</version>
               <scope>test</scope>
           </dependency>
在resources下面创建核心配置文件: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>
    <!--mybatis环境的配置-->
    <environments default="development">
        <!--通常我们只需要配置一个就可以了, id是环境的名字 -->
        <environment id="development">
            <!--事务管理器:由JDBC来管理-->
            <transactionManager type="JDBC"/>
            <!--数据源的配置:mybatis自带的连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/db4"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--加载映射文件,放到src下即可-->
        <mapper resource="userMapper.xml"/>
    </mappers>
</configuration>
在resources下创建映射文件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="com.czy.dao.UserMapper">

    <!--
        查询语句
        resultType:返回的实体类的类型,类全名
    -->
    <select id="findAll" resultType="com.czy.pojo.User">
        select * from user
    </select>
</mapper>
创建UserMaper接口
package com.czy.dao;

import com.czy.pojo.User;

import java.util.List;

public interface UserMapper {
    List<User> findAll();
}

将接口和映射文件绑定:

映射文件的namespace值 == 接口的全路径
映射文件的sql语句的id值 == 接口中的方法名

在这里插入图片描述

创建User类
package com.czy.pojo;

import java.util.Date;

public class User {

    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    public User(Integer id, String username, Date birthday, String sex, String address) {
        this.id = id;
        this.username = username;
        this.birthday = birthday;
        this.sex = sex;
        this.address = address;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

在mybatis中一个会话相当于一次访问数据库的过程,一个会话对象类似于一个connection连接对象。
1.SqlsessionFactoryBuilder:这是一个临时对象,用完就不需要了,通过这个工厂创建类来创建一个会话工厂。
2.SqlsessionFactory:从一个工厂类中得到一个会话对象,一个项目中只需要创建一个会话对象。通过会话工厂对象来创建会话对象。
3.Sqlsession:每次访问数据库都需要创建一个会话对象,这个会话对象不能共享。访问完成以后会话需要关闭。

package com.czy;

import com.czy.dao.UserMapper;
import com.czy.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class Test {

    @org.junit.Test
    public void test01() throws Exception {
        //1.从配置文件中获取SqlsessionFactory
        String resource = "mybatis-config.xml";
        // //加载核心配置文件获取输入流
        InputStream is= Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

        //2.从sqlsession中获取session
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //3.使用sqlsession获取获取接口的动态代理对象 !!!!
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.findAll();

        for (User user : userList) {
            System.out.println(user);
        }

        //关闭会话
        sqlSession.close();

    }
}

结果:
在这里插入图片描述
编写流程:Resources工具类直接可以读取src目录下配置文件,转成输入流。
在这里插入图片描述

注意:

mybatis配置文件分两种:
1.核心配置文件:mybatis-config.xml配置连接数据库参数
2.映射文件:UserMapper.xml编写sql语句

mybatis执行流程分析

在这里插入图片描述
说明:

1.第一步:是从核心配置文件mybatis-config.xml中构建SqlSessionFactory对象,由于核心配置文件mybatis-config.xml中关联了映射文件UserMapper.xml,所以在SqlSessionFactory中也存在映射文件的内容

2.第二步:是从SqlSessionFactory中获取SqlSession会话对象,其实SqlSession会话对象底层封装的就是conn连接对象

3.第三步:是通过SqlSession会话对象调用查询方法selectList然后根据参数找到映射文件中中的sql语句并将数据封装到pojo的User对象中

核心配置文件 mybatis-config.xml

可以在idea中定义模板。
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--加载外部的配置文件-->
    <properties resource="db.properties"></properties>
    <!--settings-->
    <settings>
        <!--开启驼峰自动映射-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <!--别名-->
    <typeAliases>
        <package name="com.czy.sh.pojo"></package>
    </typeAliases>
    <!--mybatis环境的配置
        一个核心配置文件,可以配置多个运行环境,default默认使用哪个运行环境
    -->
    <environments default="development">
        <!--通常我们只需要配置一个就可以了, id是环境的名字 -->
        <environment id="development">
            <!--事务管理器:由JDBC来管理-->
            <!--
                事务管理器type的取值:
                1. JDBC:由JDBC进行事务的管理
                2. MANAGED:事务由容器来管理,后期学习Spring框架的时候,所有的事务由容器管理
            -->
            <transactionManager type="JDBC"/>
            <!--数据源的配置:mybatis自带的连接池-->
            <!--
                数据源:
                1. POOLED:使用mybatis创建的连接池
                2. UNPOOLED:不使用连接池,每次自己创建连接
                3. JNDI:由服务器提供连接池的资源,我们通过JNDI指定的名字去访问服务器中资源。
            -->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

    </environments>
    <!--映射器-->
    <mappers>
        <!--加载其它的映射文件 注:注解开发是点号-->
        <!-- <package name="com.czy.sh.dao"></package>-->
        <!--加载其它的映射文件 注:不是点号-->
        <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
        <!--
            加载其它的映射文件 xml形式
                包扫描方式加载mapper映射文件,说明:
                1. 要求mapper映射文件,与mapper接口要放在同一个目录
                2. 要求mapper映射文件的名称,与mapper接口的名称要一致
            -->
        <package name="com.czy.sh.dao"></package>
    </mappers>
</configuration>

mybatis核心配置

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

1.mybatis全局配置文件是mybatis框架的核心配置,整个框架只需一个;
2.mybatis全局配置文件的配置顺序,必须按照一下顺序进行配置:
	properties:属性配置
	settins:设置
	typeAliases:类型别名设置
	typeHandlers:类型处理器
	enviroments:环境配置
		environment:环境配置
		transactionManager:事务管理器
		dataSource:数据源
	mappers:映射器

properties(属性)

作用:
1.加载外部的java资源文件(properties文件,一般是数据源的配置);
2.通过子标签property设置属性
需求:
使用properties属性,配置数据库连接参数
property标签设置
1.通过property标签设置属性;
2.使用¥{key}获取设置的属性值

新建jdbc.properties文件,将连接参数抽取到配置文件中:

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/heima
username=root
password=root

引入资源文件
在这里插入图片描述
【注意事项】:子标签和属性resource同时加载一个变量,先加载子标签,然后在加载resource关联的外部文件的值,那么子标签中的内容会被覆盖。此时子标签存在就没有意义了。

在这里插入图片描述

settings(设置)

settings参数有很多,经常用到的是mapUnderscoreToCamelCase,就是映射下划线到驼峰命名。
在这里插入图片描述

开启驼峰匹配:完成经典的数据库命名到java属性的映射
		经典数据库命名:如果多个单词,通常用下划线分割。
		java:第二个单词大写。
驼峰匹配:相当于去掉数据库中的下划线和java进行匹配。
应用场景
从数据库中查到数据的字段名user_name和实体类属性名userName,不一致,导致查到的数据无法封装到实体类中。
但是,实体类中的属性userName符合驼峰命名,数据库字段名user_name符合数据库经典字段命名。

在这里插入图片描述
如果此时直接查询数据,那么用户名是null。
在这里插入图片描述

解决方案
1.配置驼峰匹配
	在mybatis-config.xml中做如下配置:
	<settings>
 		 <setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
2.如果数据库字段名和属性名不一致,或者是也不满足上面的命名规则。可以通过在SQL语句中为字段名取别名进行映射。
	<select id = "queryAll" resultType = "com.czy.pojo.User">
		select  *,user_name as userName from user
	</select >

typeAliases(类型别名)

作用
类型别名是给类的权限定名称取一个段名称。存在的意义就在于减少类完全限定名称的长度。

在这里插入图片描述
这些全限定类名可通过设置类型别名–短名称代替。类型别名的设置有以下几种方式:

package

扫面指定包下的所有类,扫描之后的别名就是类名,不区分大小写:


<!--
        三、typeAliases(类型别名)
            【1】作用:给类的全限定名称 取一个短名称   com.heima.mybatis.pojo.User==>User
            【2】用法:
                1、单独取别名:<typeAlias type="com.heima.mybatis.pojo.User" alias="User"/>
                2、批量取别名:<package name="com.heima.mybatis.pojo"/> 扫描到当前包下的所有类
                              类的类名==》别名
-->
<typeAliases>
        <!--扫描com.itheima.sh.pojo包下所有的类,类名直接作为别名(别名不区分大小写)-->
        <package name="com.itheima.sh.pojo"/>
</typeAliases>

使用别名:
在这里插入图片描述

内置别名

这是一些为常见的 Java 类型内建的相应的类型别名。它们都是不区分大小写的,注意对基本类型名称重复采取的特殊命名风格。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

typeHandlers(类型处理器)

无论是mybatis在预处理语句(PreparedStatement) 中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成java类型,下表描述了一些默认的类型处理器
在这里插入图片描述

数据库数据类型:varchar    ===StringTypeHandler===> 实体类:String
数据库数据类型:double     ===DoubleTypeHandler===>  实体类中的数据: java.lang.Double 

environments

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

默认环境设置

第一步:在environments标签中配置多个environment,通过属性default指定一个默认环境配置:
在这里插入图片描述
第二步:在构建SqlSessionFactory时,可指定具体环境,如果不指定就使用默认的环境配置;
在这里插入图片描述

指定环境设置

第一步:在environments中设置多个环境:
在这里插入图片描述

第二步:在构建SqlSessionFactory时,通过environment的id指定环境

在这里插入图片描述

注意事项:

虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用第三方的连接池:druid,C3P0。并且使用spring来管理数据源(连接池),来做到环境的分离。

mappers(映射器)

mappers: UserMapper.xml =====>UserMapper.java接口关联
作用:维护接口和映射文件之间的关系。

方式一:加载映射文件方式resource

在mybatis-config.xml文件中,通过mapper标签的resource属性引入当前工程src下的映射文件:
在这里插入图片描述
缺点:有多个映射文件就是配置多个mapper,很麻烦。

方式二:加载接口,关联映射文件方式package

在mybatis-config.xml文件中开启包扫描:扫描包下的所有接口和对应的映射文件。

条件:
	1.接口名和映射文件名保持一致
	2.路径保持一致

在这里插入图片描述
在mybatis-config.xml配置mapper接口的全路径:

  <mappers>
        <package name="com.itheima.sh.dao"/>
    </mappers>
原理
扫描目标目录下的mapper接口,并按照class的方式找到接口对应的映射文件
要求:
 1.映射文件和mapper接口在同一目录下
 2.文件名必须一致
 3.映射文件的namespace必须和mapper接口的全路径保持一致
缺点
1、需要遵守的条件太多
2、mapper.xml和mapper.java没有分离。

编写会话工具类

  1. 在静态代码块中创建会话工厂对象
  2. 编写静态方法得到会话对象,设置自动提交事务
  3. 编写静态方法得到会话对象,方法接收调用者传递的布尔类型,决定是否自动提交事务
  4. 编写静态方法接收会话对象,手动提交事务并且关闭会话
  5. 编写静态方法接收会话对象,回滚事务并且关闭会话
package com.czy.utils;

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

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

public class SqlSessionUtil {

    private  static SqlSessionFactory sqlSessionFactory;

    static {
        //实例化工厂建造类
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //读取核心配置文件
        try {
            InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
            //创建工厂对象
            sqlSessionFactory = sqlSessionFactoryBuilder.build(is);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取会话对象,自动提交事务
     * @return
     */
    public  static  SqlSession getSession(){
        return sqlSessionFactory.openSession(true);
    }

    /**
     * 是否自动提交事务
     * @param flag
     * @return
     */
    public static  SqlSession getSession(Boolean flag){
        return sqlSessionFactory.openSession(flag);
    }

    /**
     * 提交事务并关闭session
     */
    public  static  void commitAndClose(SqlSession session){
        if (session!=null){
            session.commit();
            session.close();
        }
    }
    /**
     * 回滚事务,并关闭session
     */
    public static void rollBackAndClose(SqlSession session){
        if (session!=null){
            session.rollback();
            session.close();
        }
    }
}


测试:

public class Test {

    @org.junit.Test
    public void test01() throws Exception {
        SqlSession session = SqlSessionUtil.getSession();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> all = mapper.findAll();
        for (User user : all) {
            System.out.println(user);
        }
        SqlSessionUtil.commitAndClose(session);
    }
}

mybatis映射文件配置

mapper映射文件中定义了操作数据库的sql,每一个sql都被包含在一个statement中。映射文件是mybatis操作数据库的核心。
在这里插入图片描述

sql语句-CRUD

映射文件需要直接书写sql语句对数据库进行操作,对数据库操作sql语句主要有CRUD这四类,这四类对应到映射文件中的配置为四类标签:select,insert,update,delete


public interface UserMapper {
    List<User> findAll();

    User  findById(int id);

    void save(User user);

    void update(User user);

    void  delete(int id);
}

select

Select标签:用来编写查询语句的statement
UserMapper.xml:

<!--根据id查询用户数据-->
<!--parameterType="int" 表示sql语句参数id的类型,intInteger的别名-->
<select id="queryById" resultType="user" parameterType="int">
    select * from user where id = #{id}
</select>

insert

Insert标签:编写新增语句的statement

 <!--新增
	注意:#{username},#{birthday},#{sex},#{address} 大括号里面的值必须和pojo的实体类User类中的属性名一致,否则会报错。其实这里看的是User类中的getXxx()的get后面的xxx内容
例如 getUserName---看的是userName ,就是将get去掉,U变为小写u,后面不变
-->
<insert id = "save">
	insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert>

update

Update标签:编写更新语句的statement

 <!--
        username = #{username}
        1.等号左边表示数据表列名
        2.#{username} 这里的username需要和pojo中的get和set方法后面的标识符一致
    -->
<update id="updateUser">
        update user set username = #{username},birthday=#{birthday},sex=#{sex},address=#{address} where id = #{id}
    </update>

delete

Delete标签:

<delete id = "delete">
 delete from user where id =#{id}
</delete>

传入的参数

parameterType

CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型,入参数据有以下几种类型:hashMap,基本数据类型(包装类),实体类;
在mybatis中入参的数据类型分为2种:
1.简单数据类型:int,String,long,date
2.复杂数据类型:类和map
说明:如果传递参数是数组或者集合,底层都会封装到Map集合中。

【基本类型数据】
<!--根据id查询-->
<!--parameterType="int" 表示sql语句参数id的类型,intInteger的别名.MyBatis 可以通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。-->
<select id="queryById" resultType="user" parameterType="int">
    select * from user where id = #{id}
</select>

【pojo类型】
<insert id="savetUser" parameterType="User">
  INSERT INTO user(...)
</insert>
说明:对于parameterType属性可以不写,如果不写,mybatis会通过TypeHandler根据接口中的方法参数类型推断出具体传入语句的参数类型
自增主键

需求

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

测试类代码:

@Test
    public void saveUser() throws Exception {
        User user = new User();
        user.setUsername("蔡徐坤");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("上海");

        userMapper.saveUser(user);

        //查看新的数据的主键值
        System.out.println(user.getId());//null
    }

直接获取的结果是null。

有两种方式可以实现添加好数据之间将主键封装到实体类对象中:

实现一:使用insert标签的子标签selectKey实现;

mysql中的函数:last_insert_id() 得到最后添加的主键

<insert id="save" parameter="user">
	insert into user values(null,#{username},#{birthday},#{sex},#{address})
<!--
    keyColumn:主键在表中对应的列名
    keyProperty:主键在实体类中对应的属性名
    resultType:主键的数据类型
    order:
        BEFORE: 在添加语句前执行查询主键的语句
        AFTER: 在添加语句后执行查询主键的语句
    -->
	<selectKey keyColumn = "id" keyProperty ="id" resultType = "int" order = "AFTER">
		select last_insert_id();
	</selectKey>
</insert>	
实现二:使用insert标签的属性useGeneratedKeys,keyProperty,keyColumn实现;
<!-- useGeneratedKeys :true 获取自动生成的主键,相当于select last_insert_id()
	keyColumn:表中主键的列名
	keyProperty:实体类中主键的属性名
-->
<insert id="save2" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        insert into user values(null,#{username},#{birthday},#{sex},#{address})
</insert>

单个参数,多个参数

主要针对简单类型的数据(int,string,long,date)等数据进行入参处理。

单个参数
接口方法传入一个参数
接口参数
 User queryById(Integer id);
接收参数
1.通过#{参数名}接收
 <!--根据id查询-->
    <select id="queryById" resultType="User" parameterType="int">
        select *,user_name AS  userName from user where id = #{id}
    </select>
2. 通过#{任意变量名}接收
 <!--根据id查询-->
    <select id="queryById" resultType="User" parameterType="int">
        select *,user_name AS  userName from user where id = #{abc}
    </select>
结论
当接口方法传入一个参数时,mybatis不做特殊处理,只需要#{任意变量名}即可

多个参数

需求:根据用户名和性别查询用户

接口参数
//根据用户名和性别查询
User queryByUserNameAndSex(String userName, String sex);

###userMapper.xml

<select id="queryByUserNameAndSex" resultType="User">
        select * from user where username=#{username} and sex=#{sex}
</select>

测试类

  @Test
    public void queryByUserNameAndSex() throws Exception {
        User user = userMapper.queryByUserNameAndSex("孙悟空", "男");
        System.out.println("user = " + user);
    }

在这里插入图片描述
当传入多个参数时,mybatis底层进行了处理,我们需要按照一下几种方式解决异常:

1.使用参数索引获取:arg0,arg1
<select id ="queryByUserNameAndSex" resultType = "user">
	select * from user where username = #{arg0} and sex = #{arg1}
</select>

说明:

这里的sql语句的参数书写顺序:select * from user where username=#{arg0} and sex=#{arg1}
和User user = userMapper.queryByUserNameAndSex(“孙悟空”, “男”); 传递实参顺序一致。
也就是说:username=#{arg0} —》arg0的值是姓名孙悟空。
sex=#{arg1}—》arg1的值是性别男。

2.使用参数位置获取:param1,param2
    <!--根据用户名和性别查询-->
   <select id="queryByUserNameAndSex" resultType="User">
        select * from user where username=#{param1} and sex=#{param2}
    </select>
3.使用命名参数获取,明确指定传入参数的名称:

步骤一:在接口中传入参数时通过@Param指定参数名称

 User queryByUserNameAndSex(@Param("username") String username,@Param("sex") String sex);

步骤二:在接收参数时,通过指定的名称获取参数值

<!--根据用户名和性别查询-->
    <select id="queryByUserNameAndSex" resultType="User">
        <!--#{username}这里的username是@Param("username"),也就是说@Param("标识符")标识符是什么,这里就写什么-->
        select * from user where username=#{username} and sex=#{sex}
    </select>

pojo参数

使用pojo来传递参数

 void saveUser(User user);
<!--新增-->
<insert id="saveUser">
    insert into user values (null ,#{username},#{birthday},#{sex},#{address})
</insert>

说明:接口方法传入pojo类型的数据时,mybatis底层直接使用pojo封装数据,sql语句中#{username}取值就是到pojo中调用getUsername();

HashMap参数

需求:模拟用户登录,登录方法参数是Map集合,泛型都是String类型分别表示用户名和性别。

在UserMapper接口中添加以下方法:

/**
 * 用户登陆,参数为map
 * @return
 */
User login(Map<String,String> map);

UserMapper.xml配置文件:

<!--
   将map的key作为参数名称来传递参数
-->
<select id="login" resultType="User">
     select * from user where username=#{username} and sex=#{sex}
</select>

参数值的获取

参数值的获取指的是,statement获取接口方法中传入的参数,获取参数,有两种方式:#{}和${}

#{}和${}取值

#{}取值:

在这里插入图片描述

mybatis后台处理:

在这里插入图片描述

${}取值:

在这里插入图片描述

mybits后台处理:

在这里插入图片描述
注意:${id} 获取id值时,必须使用命名参数取值:
在这里插入图片描述

#{}取值和${}取值的小结
1.sql语句中获取参数的方式:
	#{}: select * from user where id = ?
	${}: select * from user where id = 1
2.	取值的相同点:
	都能获取接口方法传入的参数值
3.取值的不同点:
	#{}取值:是以预编译的形式将参数设置到sql语句中。防止sql的注入问题
	${}取值:直接把获取到的参数值,拼接到sql语句中,会有安全问题,不能防止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中的不是查询的条件,只能使用${}拼接;

结果映射

在使用原生的JDBC操作时,对于结果集resultSet,需要手动处理,mybatis框架提供了resultType和resultMap来对结果集进行封装。

注意:只要一个方法有返回值需要处理,那么resultType和resultMap必须有一个。

resultType
从sql语句中返回期望类型的类的完全限定名或别名。如果返回的是集合,那么应该设置集合中包含的类型,而不是集合本身。可以使用resultType或resultMap,但不能同时使用。
返回值是简单类型

在这里插入图片描述

返回值是一个pojo对象时

在这里插入图片描述

返回值是一个集合List

在这里插入图片描述

返回值是map

1.返回一条数据,封装到map中
需求:

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

接口:

 //需求:查询id是1的数据,将查询的结果封装到Map<String,Object>中
 Map<String,Object> selectByIdReturnMap(Integer id);

sql:

<select id="selectByIdReturnMap" resultType="map">
        select * from user where id=#{id}
</select> 

结果:

map = {birthday=1980-10-24, address=花果山水帘洞, sex=, id=1, username=孙悟空}

通过上面的测试,我们可以看到,如果返回一条数据放到Map中,那么列明会作为Map集合中的key,结果作为map中的value。

返回多条数据封装到map中

需求:

查询数据表所有的数据封装到Map<String,User>集合中
要求key值为一条记录的主键,value值为pojo对象

接口:在接口上面通过@MapKey指定key值封装到列数据

 @MapKey("id")
Map<Integer,Object> selectUserByIdReturnMap2();

sql

<select id="selectUserByIdReturnMap2" resultType="map">
        select * from user
    </select>

结果:

{
	1={birthday=1980-10-24, address=花果山水帘洞, sex=, id=1, username=孙悟空}, 
	2={birthday=1992-11-12, address=白虎岭白骨洞, sex=, id=2, username=白骨精}, 
	3={birthday=1983-05-20, address=福临山云栈洞, sex=, id=3, username=猪八戒},
	4={birthday=1995-03-22, address=盤丝洞, sex=, id=4, username=蜘蛛精},
	5={birthday=2022-06-14, address=河南, sex=, id=5, username=张三}, 
	6={birthday=2022-06-14, address=河南, sex=, id=6, username=李四}, 
	7={birthday=2022-06-14, address=河南, sex=, id=7, username=王五}
}

resultMap

ResultMap是mybatis中最重要最强大的元素,使用ResultMap可以解决两大问题:

  1. POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式,比如id和userId)
  2. 完成高级查询,比如说,一对一、一对多、多对多。(后面多表中会涉及到)

查询数据的时候,查不到userName的信息,原因:数据库的字段名是user_name,而POJO中的属性名字是userName,两端不一致,造成mybatis无法填充对应的字段信息。

解决方案一:在sql语句中使用别名
<select id="queryById" resultType="user" parameterType="int">
   select *,name as username from user where id = #{id}
</select>
解决方案二:在mybatis-config.xml配置文件中设置驼峰匹配
<settings>
   <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

注意:这种方式只能解决列名是下划线命名。

解决方案三:resultMap自定义

步骤一:将驼峰匹配注释掉
步骤二:配置resultMap
在映射文件中自定义结果集类型:

<!--type 表示结果集封装的类型 
	autoMapping属性的值:
		为true时:在字段和属性名称相同时,会进行自动映射。如果不配置,则默认为true。
		为false时:只针对resultMap中已经配置的字段作映射。
	-->
<resultMap id="userResultMap" type="user" autoMapping="true">
        <!--配置主键映射关系-->
        <id column="id" property="id"></id>
        <!--配置用户名的映射关系  column 表示数据表列  property表示pojo的属性-->
        <result column="username" property="username" ></result>
    </resultMap>

步骤三:修改查询语句

<select id="findAll" resultMap="userResultMap">
        select * from user
</select>

动态sql

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

例如:
需求:查询男性用户,如果输入了用户名,按用户名模糊查询,如果没有输入用户名,就查询所有男性用户

正常的sql语句:查询男性并且用户名中包含zhang

select * from tb_user where sex = "男" and user_name like '%zhang%'
或
select * from tb_user where  sex = "男"  

实现需求时还要判断用户是否输入用户名来做不同的查询要求,而这里似乎没有办法判断是否输入了用户名,因此可以考虑使用动态sql来完成这个功能。

if标签

格式:

 <if test="判断条件">
   满足条件执行的代码
 </if>
 说明:
 1)if标签:判断语句,用于进行逻辑判断的。如果判断条件为true,则执行if标签的文本内容
 2)test属性:用来编写表达式,支持ognl;

接口:

   /**
     * 根据用户名模糊查询
     * @param userName
     * @return
     */
    List<User> queryLikeUserName(@Param("userName") String userName);

sql:

<select id="queryLikeUserName" resultType="user">
       select * from user where sex='男'
       <if test="userName!=null and userName.trim()!=''">
           and username like '%${userName}%'
       </if>
   </select>

注意:<if> 判断中:

1、if标签:用来判断;

2、test属性:使用OGNL表达式,完成具体的判断业务逻辑;

3、这里使用的字符串拼接,所以这里不能是#取值,只能使用$取值,否则会报错

测试:

有名字

在这里插入图片描述

没有名字

在这里插入图片描述

choose,when,otherwise

choose标签:分支选择(多选一,遇到成立的条件即停止)
	when子标签:编写条件,不管有多少个when条件,一旦其中一个条件成立,后面的when条件都不执行。
           test属性:编写ognl表达式
	otherwise子标签:当所有条件都不满足时,才会执行该条件

需求:

编写一个查询方法,设置两个参数,一个是用户名,一个是住址。

根据用户名或者住址查询所有男性用户:
	如果输入了用户名则按照用户名模糊查找,
	否则就按照住址查找,两个条件只能成立一个,
	如果都不输入就查找用户名为“孙悟空”的用户。

接口:

 /*
    查询用户名或者地址
  */
 List<User> queryByUserNameOrAddress(@Param("userName") String userName, @Param("address") String address);

sql

<select id="queryByUserNameOrAddress" resultType="user">
       select * from user where sex='男'
       <choose>
           <when test="userName !=null and userName.trim() != ''">
              and  username like '${userName}%'
           </when>
           <when test="address !=null and address.trim() != ''">
               and address = #{address}
           </when>
           <otherwise>
               and username = '孙悟空'
           </otherwise>
       </choose>
   </select>

where

where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字
案例:按照如下条件查询所有用户,

如果输入了用户名按照用户名进行查询,
如果输入住址,按住址进行查询,
如果两者都输入,两个条件都要成立。

接口

List<User> queryByUserNameAndAge(@Param("userName") String userName, @Param("address") String address);

sql

<select id="queryByUserNameAndAge" resultType="user">
        SELECT * FROM  user
        <where>
            <if test="userName != null and userName.trim()!=''">
                username = #{userName}
            </if>
            <if test="address!=null and address.trim()!=''">
                AND address = #{address}
            </if>
        </where>
    </select>

set

set标签:在update语句中,可以自动添加一个set关键字,并且会将动态sql最后多余的逗号去除。

案例:修改用户信息,如果参数user中的某个属性为null,则不修改。

如果在正常编写更新语句时,如下:
在这里插入图片描述

update user SET username = ?, birthday=?, sex=?, where id = ? 

那么一旦在传递的参数中没有address,此时生成的sql语句就会因为多了一个逗号而报错。

接口

void updateSelectiveUser(User user);

sql

    <!--选择性地对user数据进行修改-->
   <update id="updateSelectiveUser">
        update user
        <set>
            <if test="username != null and username.trim()!=''">
                username = #{username},
            </if>
            <if test="birthday != null">
                birthday=#{birthday},
            </if>
            <if test="sex != null and sex.trim()!=''">
                sex=#{sex},
            </if>
            <if test="address != null and address.trim()!=''">
                address=#{address}
            </if>
        </set>
        where id = #{id}
    </update>

测试

 @Test
    public void updateSelectiveUser() {
        User user = new User();
        user.setUsername("锁哥1");
        user.setBirthday(new Date());
        user.setSex("男");
        user.setAddress("");

        user.setId(7);
        userMapper.updateSelectiveUser(user);
    }

结果

 update user SET username = ?, birthday=?, sex=? where id = ? 

foreach

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

需求:按照id值是1,2,3来查询用户数据;
接口:

List<User> queryByIds(@Param("arrIds") Integer[] arrIds);

这里一定加@Param(“arrIds”),否则报错

sql

<!--根据多个id值查询-->
    <select id="queryByIds" resultType="user">
        SELECT * FROM  user WHERE id IN
        <foreach collection="arrIds" item="ID" separator="," open="(" close=")">
            #{ID}
        </foreach>
    </select>

mybatis的高级查询

sql准备:

/*
Navicat MySQL Data Transfer

Source Server         : localhost
Source Server Version : 50622
Source Host           : localhost:3306
Source Database       : heima81

Target Server Type    : MYSQL
Target Server Version : 50622
File Encoding         : 65001

Date: 2019-08-20 17:08:12
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for tb_item
-- ----------------------------
DROP TABLE IF EXISTS `tb_item`;
CREATE TABLE `tb_item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `item_name` varchar(32) NOT NULL COMMENT '商品名称',
  `item_price` float(6,1) NOT NULL COMMENT '商品价格',
  `item_detail` text COMMENT '商品描述',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_item
-- ----------------------------
INSERT INTO `tb_item` VALUES ('1', 'iPhone 6', '5288.0', '苹果公司新发布的手机产品。');
INSERT INTO `tb_item` VALUES ('2', 'iPhone 6 plus', '6288.0', '苹果公司发布的新大屏手机。');

-- ----------------------------
-- Table structure for tb_order
-- ----------------------------
DROP TABLE IF EXISTS `tb_order`;
CREATE TABLE `tb_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `order_number` varchar(20) NOT NULL COMMENT '订单号',
  PRIMARY KEY (`id`),
  KEY `FK_orders_1` (`user_id`),
  CONSTRAINT `FK_orders_1` FOREIGN KEY (`user_id`) REFERENCES `tb_user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_order
-- ----------------------------
INSERT INTO `tb_order` VALUES ('1', '1', '20140921001');
INSERT INTO `tb_order` VALUES ('2', '2', '20140921002');
INSERT INTO `tb_order` VALUES ('3', '1', '20140921003');

-- ----------------------------
-- Table structure for tb_orderdetail
-- ----------------------------
DROP TABLE IF EXISTS `tb_orderdetail`;
CREATE TABLE `tb_orderdetail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_id` int(32) DEFAULT NULL COMMENT '订单号',
  `item_id` int(32) DEFAULT NULL COMMENT '商品id',
  `total_price` double(20,0) DEFAULT NULL COMMENT '商品总价',
  `status` int(11) DEFAULT NULL COMMENT '状态',
  PRIMARY KEY (`id`),
  KEY `FK_orderdetail_1` (`order_id`),
  KEY `FK_orderdetail_2` (`item_id`),
  CONSTRAINT `FK_orderdetail_1` FOREIGN KEY (`order_id`) REFERENCES `tb_order` (`id`),
  CONSTRAINT `FK_orderdetail_2` FOREIGN KEY (`item_id`) REFERENCES `tb_item` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_orderdetail
-- ----------------------------
INSERT INTO `tb_orderdetail` VALUES ('1', '1', '1', '5288', '1');
INSERT INTO `tb_orderdetail` VALUES ('2', '1', '2', '6288', '1');
INSERT INTO `tb_orderdetail` VALUES ('3', '2', '2', '6288', '1');
INSERT INTO `tb_orderdetail` VALUES ('4', '3', '1', '5288', '1');

-- ----------------------------
-- Table structure for tb_user
-- ----------------------------
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(100) DEFAULT NULL COMMENT '用户名',
  `password` varchar(100) DEFAULT NULL COMMENT '密码',
  `name` varchar(100) DEFAULT NULL COMMENT '姓名',
  `age` int(10) DEFAULT NULL COMMENT '年龄',
  `sex` int(11) DEFAULT NULL COMMENT '0-女 1-男',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of tb_user
-- ----------------------------
INSERT INTO `tb_user` VALUES ('1', 'zhangsan', '123456', '张三', '30', '1');
INSERT INTO `tb_user` VALUES ('2', 'lisi', '123456', '李四', '21', '0');
INSERT INTO `tb_user` VALUES ('3', 'wangwu', '123456', '王五', '22', '1');
INSERT INTO `tb_user` VALUES ('4', 'zhangwei', '123456', '张伟', '20', '1');
INSERT INTO `tb_user` VALUES ('5', 'lina', '123456', '李娜', '28', '0');
INSERT INTO `tb_user` VALUES ('6', '蔡徐坤', '123', '小菜', '18', '1');

在这里插入图片描述

实体类

在这里插入图片描述
Item:


package com.czy.pojo;

public class Item {
    private int id;
    private String itemName;
    private double itemPrice;
    private String itemDetail;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public double getItemPrice() {
        return itemPrice;
    }

    public void setItemPrice(double itemPrice) {
        this.itemPrice = itemPrice;
    }

    public String getItemDetail() {
        return itemDetail;
    }

    public void setItemDetail(String itemDetail) {
        this.itemDetail = itemDetail;
    }

    @Override
    public String toString() {
        return "Item{" +
                "id=" + id +
                ", itemName='" + itemName + '\'' +
                ", itemPrice=" + itemPrice +
                ", itemDetail='" + itemDetail + '\'' +
                '}';
    }
}

order

package com.czy.pojo;

public class Order {
    private Integer id;
    private String orderNumber;
    //关联User对象
    private User user;
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getOrderNumber() {
        return orderNumber;
    }
    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", orderNumber='" + orderNumber + '\'' +
                ", user=" + user +
                '}';
    }
}

orderDetail

package com.czy.pojo;

public class OrderDetail {

    private int id;
    private int orderId;
    private int itemId;
    private double totalPrice;
    private String status;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    public int getItemId() {
        return itemId;
    }

    public void setItemId(int itemId) {
        this.itemId = itemId;
    }

    public double getTotalPrice() {
        return totalPrice;
    }

    public void setTotalPrice(double totalPrice) {
        this.totalPrice = totalPrice;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    @Override
    public String toString() {
        return "OrderDetail{" +
                "id=" + id +
                ", orderId=" + orderId +
                ", itemId=" + itemId +
                ", totalPrice=" + totalPrice +
                ", status='" + status + '\'' +
                '}';
    }
}

user

package com.czy.pojo;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

public class User implements Serializable {

    private Long id;

    // 用户名
    private String userName;

    // 密码
    private String password;

    // 姓名
    private String name;

    // 年龄
    private Integer age;
    //0 女性 1 男性
    private Integer sex;

    //订单
    List<Order> orders;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public List<Order> getOrders() {
        return orders;
    }

    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", orders=" + orders +
                '}';
    }
}

测试接口:

public interface UserMapper {
    //完成根据id查询用户数据;
    User selectById(Long id);
}

sql:

   <!--根据id查询:statement-->
    <select id="selectById"  resultType="User">
        SELECT *  FROM  tb_user WHERE  id=#{id}
    </select>

表之间关系介绍

在这里插入图片描述

1.tb_user和 tb_order表关系
	tb_user  《==》  tb_order:一对多, 一个人可以下多个订单
	tb_order 《==》 tb_user:一对一,一个订单只能属于一个人
	结论:tb_user和tb_order属于一对多的关系,需要将一方tb_user的主键作为多方tb_order的外键维护关系
2.tb_order 和 tb_item 表关系
	tb_order 《==》 tb_item :一个订单可以有多个商品
	tb_item 《==》 tb_order:一个商品可以在多个订单上
	结论:tb_order和tb_item属于多对多的关系,需要创建中间表tb_orderdetail维护两个表的关系,并且将两张表	的主键作为中间表的外键

一对一查询

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

订单接口:

 <select id="queryOrderAndUserByOrderNumber2" resultType="order">
        select * from tb_order as order inner join tb_user as user on  order.user_id = user.id where user.id = #{orderNumber}
    </select>

注意:需要在order实体类中添加User属性

mybatis-config.xml配置文件中将orderMapper.xml添加到映射器中

在这里插入图片描述
sql:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
映射文件
namespace 指定接口的类全名
-->
<mapper namespace="com.itheima.sh.dao.OrderMapper">
    <!--
        1.autoMapping="true" 表示只需要给当前表的id然后自动映射当前表的其他列值到
        对应实体类的属性中,这属于偷懒行为,开发中我们最好都书写出来
        2.id标签表示id的映射关系
        3.result标签表示其他列和pojo类的属性映射关系
        4.一对一映射关系使用子标签association来表示引用的另一个pojo类的对象
    -->
    <resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
        <!--主表主键-->
        <id column="id" property="id"/>
        <!--关联关系-->
        <!--
            1.property="user" 表示在Order类中的引用的User类的对象成员变量名
            2.javaType="User" 表示引用的user对象属于User类型
        -->
        <association property="user" javaType="User" autoMapping="true">
            <!--从表主键-->
            <id column="id" property="id"/>
            <!--<result column="user_name" property="userName"/>-->
        </association>
    </resultMap>

    <!--多表关联查询:一对一-->
    <select id="queryOrderAndUserByOrderNumber2" resultMap="orderAndUserResultRelative">
        SELECT
            *
        FROM
            tb_order tbo
            INNER JOIN tb_user tbu ON tbo.user_id = tbu.id
        WHERE
            tbo.order_number = #{orderNumber}
    </select>
</mapper>

结果
在这里插入图片描述
注意:
在这里插入图片描述
因为tb_user表的主键是id,tb_order的主键也是id。查询的结果中有两列相同的id字段。在将查询结果封装到实体类的过程中就会封装错误。

解决方案:

1、建议将所要查询的所有字段显示地写出来;
2、将多表关联查询结果中,相同的字段名取不同的别名;

在这里插入图片描述

resultMap中应该如下配置:

在这里插入图片描述

一对多查询

需求:查询id为1的用户及其订单信息

分析:

一个用户可以有多个订单
用户(1)  ----  订单(n)
需要在user实体类中添加集合型的订单

接口:

  /**
     * 根据用户id查询用户及其订单信息
     * @param id
     * @return
     */
    User oneToManyQuery(@Param("id") Long id);

sql

   <!--自定义结果集-->
    <resultMap id="oneToManyResult" type="User" autoMapping="true">
        <!--User的主键-->
        <id column="uid" property="id"/>
        <!--Order关联映射-->
        <!--
            1.一对多使用collection子标签进行关联多方Order
            2.属性:
                1)property="orders" 这里的orders表示User类的成员变量orders
                2)javaType="List" 表示User类的成员变量orders存储的Order对象使用的类型,这里是List,可以不配置
                3) ofType="Order" 表示List集合中存储数据的类型 Order
        -->

        <collection property="orders" javaType="List" ofType="Order" autoMapping="true">
            <!--Order的主键-->
            <id column="oid" property="id" />
        </collection>
    </resultMap>

    <!--根据用户ID查询用户及其订单数据-->
    <select id="oneToManyQuery" resultMap="oneToManyResult">
        SELECT
            tbo.id as oid,
            tbo.order_number,
            tbu.id as uid,
            tbu.user_name,
            tbu.password,
            tbu.name,
            tbu.age,
            tbu.sex
        FROM
            tb_user tbu
            INNER JOIN tb_order tbo ON tbu.id = tbo.user_id
        WHERE
            tbu.id = #{id}
    </select>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值