黑马程序员——mysql——day08——MyBaits实现动态sql、多表查询和注解开发

目录:

  1. 学习目标
  2. 回顾
  3. 动态SQL
    1. if标签
      1. 定义接口方法
      2. 编写SQL
      3. 测试
    2. choose,when,otherwise
      1. 定义接口方法
      2. 编写SQL
      3. 测试
    3. where
      1. 定义接口方法
      2. 编写SQL
      3. 测试
    4. set
      1. 定义接口方法
      2. 编写SQL
      3. 测试
    5. foreach
      1. 定义接口方法
      2. 编写SQL
      3. 测试
    6. 小结
  4. mybatis高级查询【掌握】
    1. 准备工作
      1. 【1】包结构
      2. 【2】导入SQL脚本
      3. 【3】创建实体来包,导入资料中的pojo
      4. 【4】UserMapper接口
      5. 【5】UserMapper.xml
      6. 【6】测试
    2. 表介绍和表关系说明
    3. 一对一查询
      1. 关联查询
      2. 第一步:需求分析
      3. 第二步:添加关联
      4. 第三步:添加方法
      5. 第四步:编写SQL
      6. 第五步:测试
      7. 注意事项
    4. 一对多查询
      1. 第一步:需求分析
      2. 第二步:添加映射关系
      3. 第三步:编写接口方法
      4. 第四步:编写SQL
      5. 第五步:测试
    5. 多对多
      1. 第一步:【需求分析】
      2. 第二步:添加关联关系
      3. 第三步:编写接口方法
      4. 第四步:编写SQL
      5. 第五步:测试
      6. 扩展
        1. 编写接口方法
        2. 测试
  5. ResultMap继承
  6. 高级查询小结
  7. mybatis延迟加载
    1. 延迟加载概述
    2. 开启延迟加载
    3. 延迟加载测试
      1. 分析:
      2. 第一步:编写接口方法
      3. 第二步:编写SQL
      4. 第三步:开启懒加载
      5. 第四步:测试
  8. mybatis注解开发
    1. 概述
    2. 注解实现CRUD
      1. CRUD相关注解
      2. 新增
      3. 删除
      4. 修改
      5. 查询
    3. 返回新增数据的id(自增主键回填)
    4. 注解实现别名映射
  9. 总结

1.学习目标

  1. 掌握sqlMapConfig.xml中常用标签
  2. 掌握mybatis框架在DAO层的开发
  3. 能够完成单表的CRUD操作
  4. 掌握mybatis框架的输入输出映射
  5. 掌握MyBatis动态SQL

2.回顾

1、数据库: DataBase-数据存储
2、数据操作:CRUD-增删改查
    【1】使用MySQL默认的客户端:  输入sql脚本操作数据
    【2】JDBC:java代码操作数据库中的数据(sql脚本)

3、JDBC:
    【1】导入mysql驱动包: java程序==》数据库软件
    【2】编码:JDBC
            //1、注册驱动:Class.forName("com.mysql.jdbc.Driver");
            //2、获取连接:Connection conn = DriverManager.getConnection(url,user,password);
            //3、创建操作:
                    String sql = "SELECT * FROM user WHERE id = ?";
                    PreparedStatement stmt = conn.preparedStatement(sql);
                    stmt.setInt(1,1);
            //4、执行sql语句:
                    ResultSet rs = stmt.executeQuery();
            //5、处理结果集:
                  while(rs.next()){
                    int id =     rs.getInt("id");
                    String username = rs.getString("username");
                    //获取其他字段数据..........
                    
                    //封装数据: User user = new User();
                     user.setId(id);
                     user.setUsername(username);
                  }
            //6、释放资源:close()

4、持久层框架:对JDBC进行封装--mybatis
        1、导入mybatis相关jar包;
        2、提供配置文件;
        3、框架开发;

 

3.动态SQL

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

例如,下面需求就会使用到拼接sql语句:

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

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

 

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

select * from tb_user where  sex = "男"  

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

动态 SQL 元素和后面学习的 JSTL 或基于之前学习的类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 开始精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的 OGNL 的表达式来淘汰其它大部分元素。

常见标签如下:

if:判断   if(1 gt 2){}
choose (when, otherwise):分支判断    switch:多选一
trim (where, set):去除
foreach:循环遍历标签

动态SQL中的业务逻辑判断需要使用到以下运算符: 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对象属性值
13.   e1[ e2 ]按索引取值,List,数组和Map
14.   @class@method(args)调用类的静态方法
15.   @class@field调用类的静态字段值
if标签

格式:

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

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

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

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

select * from tb_user where  sex = "男"  

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

上述动态sql语句部分: and user_name like '%zhang%'

定义接口方法

在UserMapper接口中,定义如下方法:

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

在UserMapper.xml文件中编写与方法名同名的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、这里使用的字符串拼接,所以这里不能是#取值,只能使用$取值,否则会报错

测试

【userName有值】

对应的SQL语句是:select * from user where sex="男" and username like '%孙%'

【userName没有值】

 

对应的SQL语句是:select * from user where sex="男"

【小结】

1、if标签:用来在sql中处理判断是否成立的情况;
2、属性:test中书写OGNL表达式,如果结果为true,if标签的文本中的内容会被拼接到SQL中,反之不会被拼接到SQL中;
3、if标签的应用场景:适用于 二选一 
choose,when,otherwise

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

需求:

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

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

【需求分析】

1、查询所有男性用户,如果输入了用户名则按照用户名模糊查找;

SELECT * FROM  user WHERE  sex = "男" AND  username LIKE  '%孙%';

2、查询所有男性用户,如果输入了住址则按照住址查询;

SELECT * FROM  user WHERE  sex = "男" AND  address = "花果山水帘洞";

3、查询所有男性用户,如果都不输入就查找用户名为“孙悟空”的用户。

SELECT * FROM  user WHERE  sex = "男" AND username = '孙悟空';
定义接口方法

在UserMapper接口中,定义接口方法:

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

在UserMapper.xml中编写对应的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>
测试

编写测试类,对这个方法进行测试:

@Test
public void queryByUserNameOrAddress(){
    List<User> userList = userMapper.queryByUserNameOrAddress("", null);
    System.out.println("userList = " + userList);
}

【小结】

choose,when,otherwise标签组合的作用类似于java中的switch语句,使用于多选一;

where

where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字

案例:按照如下条件查询所有用户,

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

【需求分析】

1、如果输入了用户名按照用户名进行查询,

SELECT * FROM  user WHERE user_name = '孙悟空';

2、如果输入住址,按住址进行查询,

SELECT * FROM  user WHERE address='花果山水帘洞';

3、如果两者都输入,两个条件都要成立。

SELECT * FROM  user WHERE user_name = '孙悟空' AND address='花果山水帘洞';
定义接口方法

在UserMapper接口中定义如下方法:

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

在UserMapper.xml中编写SQL

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

        说明:如果按照如下写sql语句会有问题,假设用户名username是空,那么用户名的sql语句不参与条件,此时
        sql语句就会变为:SELECT * FROM  user where AND address = #{address}
        where后面直接书写了and显然不满足sql语句语法,这里会报错
        我们可以使用where标签解决上述问题:
             where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字
    -->
   <!-- <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>
    </select>-->
    <!-- SELECT * FROM user WHERE address = ?
        where子标签将and去掉了
    -->
    <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>

说明:

1.说明:如果按照如下写sql语句会有问题,假设用户名username是空,那么用户名的sql语句不参与条件,此时 sql语句就会变为:SELECT * FROM user where AND address = #{address} where后面直接书写了and显然不满足sql语句语法,这里会报错 我们可以使用where标签解决上述问题: where标签:拼接多条件查询时 1、能够添加where关键字; 2、能够去除多余的and或者or关键字

2.SELECT * FROM user WHERE address = ? where子标签将and去掉了

测试
@Test
    public void queryByUserNameAndAge() {
        List<User> userList = userMapper.queryByUserNameAndAge("", "花果山水帘洞");
        System.out.println("userList = " + userList);
    }

 

只传入住址,此时where子标签去掉了and.

【小结】

1、<where>标签作用:用于拼接多选一或者同时成立的SQL情况;
2、<where>还会根据情况,动态的去掉SQL语句中的AND或者or;

 

set

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

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

如果在正常编写更新语句时,如下:

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

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

定义接口方法

在UserMapper接口中定义如下方法:

void updateSelectiveUser(User user);
编写SQL

在UserMapper.xml文件中编写如下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 = ? 

【小结】

1、<set>标签替代了sql语句中的set关键字;
2、<set>标签还能把sql中多余的,去掉;
foreach
foreach标签:遍历集合或者数组
<foreach collection="集合名或者数组名" item="元素" separator="标签分隔符" open="以什么开始" close="以什么结束">
   #{元素}
</foreach>
	collection属性:接收的集合或者数组,集合名或者数组名
	item属性:集合或者数组参数中的每一个元素 
	separator属性:标签分隔符 
	open属性:以什么开始 
	close属性:以什么结束

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

定义接口方法

在UserMapper接口中定义如下方法:

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>
测试
    @Test
    public void queryByIds() {
        Integer[] arrIds = {1,2,3};
        List<User> userList = userMapper.queryByIds(arrIds);
        System.out.println("userList = " + userList);
    }

【小结】

<foreach>标签的作用:用于对查询参数进行遍历取值;
小结

If标签:条件判断
    test属性:编写ognl表达式

where标签:用于sql动态条件拼接,添加where关键字,可以将动态sql多余的第一个and或者or去除。

set标签: 用于更新语句的拼接,添加set关键字,并可以将动态sql中多余的逗号去除

foreach标签:用于遍历参数中的数组或者集合
    collection属性:参数中的数组或者集合
    item属性:表示数组或者集合中的某个元素,取出数据使用#{item的属性值}
    separator属性:分隔符
    open:以什么开始
    close:以什么结束

4.mybatis高级查询【掌握】

准备工作
【1】包结构

创建java项目,导入jar包和log4j日志配置文件以及连接数据库的配置文件;

【2】导入SQL脚本

运行资料中的sql脚本:mybatis.sql

 

【3】创建实体来包,导入资料中的pojo

【4】UserMapper接口
package com.itheima.sh.dao;
import com.itheima.sh.pojo.User;
public interface UserMapper {
    //完成根据id查询用户数据;
    User selectById(Long id);
}
【5】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.heima.mybatis.mapper.UserMapper">

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

</mapper>
【6】测试
package com.itheima.sh.test;

import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.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 org.junit.BeforeClass;
import org.junit.Test;

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

public class MybatisTest01 {
    private static UserMapper mapper = null;

    @BeforeClass
    public static void beforeClass() throws Exception {
        //1.构建SessionFactory
        String resouce = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resouce);
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
        //2.获取session
        SqlSession sqlSession = build.openSession(true);
        //3.获取接口对象
        mapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void selectById() {
        User user = mapper.selectById(1L);
        System.out.println(user);
    }
}
表介绍和表关系说明

导入资料中mybatis.sql脚本。新建以下4张表

tb_user:用户表
tb_order:订单表
tb_item:商品表
tb_orderdetail:订单详情表

 【表关系】

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查询出订单信息,并查询出下单人信息。

关联查询

【目标】使用多表关联查询,完成根据订单号查询订单信息和下单人信息(订单号20140921003)

【分析】

  • 一个订单编号对应一个订单,一个订单只能属于一个人。所以上述需求实现是一对一的实现。

【步骤】

1、首先,编写接口方法。编写SQL语句;
2、第二步:分析SQL,封装数据(关联对象);
3、处理多表之间的数据封装(数据库字段名---》实体类的属性名之间的映射)

第一步:需求分析

编写多表关联查询SQL,根据订单号查询订单信息及下单人信息;

查询语句以及查询结果:

    #方式一:分步查询
	#第一步:根据order_number查询订单信息;
	SELECT * FROM tb_order WHERE order_number = '20140921003';
	#第二步:根据订单信息中的user_id查询出下单人的信息;
	SELECT * FROM tb_user WHERE id = 1;

  #方式二:多表关联查询,内连接
	SELECT * FROM tb_order tbo inner join tb_user tbu on tbo.user_id = tbu.id where 
  tbo.order_number='20140921003'
	#多表数据封装问题:
	#关联对象封装数据(在Order中引用User)
第二步:添加关联

修改Order:

在Order类中,添加关联对象User,并添加getter和setter方法;

package com.itheima.sh.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 +
                '}';
    }
}
第三步:添加方法

编写OrderMapper接口

public interface OrderMapper {
       /**
     * 根据订单号查询订单及下单人的信息:方式二
     * @param orderNumber
     * @return
     */
    Order queryOrderAndUserByOrderNumber2(@Param("orderNumber")String orderNumber);
}
第四步:编写SQL

在OrderMapper.xml中编写对应的SQL,并将OrderMapper.xml加入到mybatis-config.xml全局配置中;

 

【OrderMapper.xml代码】

说明:

association:配置关联对象(User)的映射关系
 <association property="user" javaType="User" autoMapping="true">
           
  </association>
	属性:
		property:关联对象在主表实体类中的属性名;property="user" 表示在Order类中的引用的User类的对象		   成员变量名
		javaType:关联对象的类型;javaType="User" 表示引用的user对象属于User类型
<?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>

说明:

1、由于queryOrderAndUserByOrderNumber2查询的结果Order对象中需要封装User信息,所以返回值不能够再使用单纯的resultType来操作;

2、定义resultMap进行关联查询的配置,其中:
    属性:
        id:标识这个resultMap;
        type:返回的结果类型
        autoMapping="true": 表示只需要给当前表的id然后自动映射当前表的其他列值到对应实体类的属性中,这          属于偷懒行为,开发中我们最好都书写出来
    子元素:
        id:主表主键映射
        result:主表普通字段的映射
        association:关联对象的映射配置
        
3、association:配置关联对象(User)的映射关系
    属性:
        property:关联对象在主表实体类中的属性名;property="user" 表示在Order类中的引用的User类的对象           成员变量名
        javaType:关联对象的类型;javaType="User" 表示引用的user对象属于User类型

第五步:测试
package com.itheima.sh.test;

import com.itheima.sh.dao.OrderMapper;
import com.itheima.sh.dao.UserMapper;
import com.itheima.sh.pojo.Order;
import com.itheima.sh.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 org.junit.BeforeClass;
import org.junit.Test;
import java.io.InputStream;
public class MybatisTest02 {
    private static OrderMapper mapper = null;
    @BeforeClass
    public static void beforeClass() throws Exception {
        //1.构建SessionFactory
        String resouce = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resouce);
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
        //2.获取session
        SqlSession sqlSession = build.openSession(true);
        //3.获取接口对象
        mapper = sqlSession.getMapper(OrderMapper.class);
    }
    @Test
    public void selectById() {
        Order order = mapper.queryOrderAndUserByOrderNumber2("20140921003");
        System.out.println("order = " + order);
    }
}

 【测试结果】

注意事项

通过上述测试结果,我们发现User的id是错误的,不是3,正确结果是1:

因为tb_user表的主键是id,tb_order的主键也是id。查询的结果中有两列相同的id字段。在将查询结果封装到实体类的过程中就会封装错误。

注意:user表查询的是id不是id1,由于SQLyog图形化界面显示的原因。可以在cmd窗口查看结果:

 【解决方案】

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

 

 

resultMap中应该如下配置:

 【正确结果】

【小结】

 一对一关联查询:
1、需要在Order实体类中关联User对象;最终将数据封装到Order中;
2、在OrderMapper.xml文件中书写关联语句并配置关系;
3、关联关系配置:
        <resultMap id="orderAndUserResultRelative" type="Order" autoMapping="true">
            <!--主表主键-->
            <id column="oid" property="id"/>
            <!--关联关系-->
            <association property="user" javaType="User" autoMapping="true">
                <!--从表主键-->
                <id column="uid" property="id"/>
            </association>
        </resultMap>

一对多查询

【目标】查询id为1的用户及其订单信息

【分析】

  • 一个用户可以有多个订单。
  • 一个订单只能属于一个用户。
  • 用户(1)-----订单(n)

【步骤】

第一步:查询SQL分析;
第二步:添加关联关系;
第三步:编写接口方法;
第四步:编写映射文件;
第五步:测试

【实现】

第一步:需求分析

编写SQL实现查询id为1的用户及其订单信息

查询语句及查询结果:

#查询id为1的用户及其订单信息
select * from tb_user where id=1;
select * from tb_order where user_id=1;

#一对多 内连接查询
select * from tb_user tbu inner join tb_order tbo on tbu.id = tbo.user_id where tbu.id=1;
# 封装数据:关联对象,一个用户关联多个订单  User(List<Order> orderList)

说明:一个用户关联多个订单 User(List<Order> orderList) ,在User类中定义一个List集合存储多个订单Order对象。

第二步:添加映射关系

因为一个用户可以拥有多个订单,所以用户订单一对多的关系;需要在User类中添加一个List<Order> 属性;

package com.itheima.sh.pojo;

import java.io.Serializable;
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 +
                '}';
    }
}
第三步:编写接口方法

UserMapper接口中,添加关联查询;

  /**
     * 根据用户id查询用户及其订单信息
     * @param id
     * @return
     */
    User oneToManyQuery(@Param("id") Long id);
第四步:编写SQL

UserMapper.xml文件中编写SQL语句完成一对多的关联查询;

说明:

1.一对多使用collection子标签进行关联多方Order
  <collection property="类中引用多方的成员变量名" javaType="存放多方容器的类型" ofType="多方类型" autoMapping="true">
  </collection>
2.属性:
    1)property="orders" 这里的orders表示User类的成员变量orders
    2)javaType="List" 表示User类的成员变量orders存储的Order对象使用的类型,这里是List 一般不书写
    3) ofType="Order" 表示List集合中存储数据的类型 Order
3.一定要记住这里给user表的id起别名是uid,order表的id起别名是oid.在resultMap标签的id子标签中的column属性值书写对应的uid和oid.

<!--自定义结果集-->
    <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>
第五步:测试

在用户的测试类中

public class MybatisTest01 {
    private static UserMapper mapper = null;

    @BeforeClass
    public static void beforeClass() throws Exception {
        //1.构建SessionFactory
        String resouce = "mybatis-config.xml";
        InputStream is = Resources.getResourceAsStream(resouce);
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(is);
        //2.获取session
        SqlSession sqlSession = build.openSession(true);
        //3.获取接口对象
        mapper = sqlSession.getMapper(UserMapper.class);
    }  
	//根据用户ID查询用户及其订单数据
    @Test
    public void oneToManyQuery() {
        User user = mapper.oneToManyQuery(1L);
        System.out.println("user = " + user);
    }
}

【小结】

一对多关系配置:

  1. 在对象中添加映射关系;
  2. 编写接口方法,编写SQL;
  3. 编写resultMap处理数据库字段和实体类之间数据的封装;

多对多

【需求】:查询订单号为20140921001的订单的详情信息即查询订单信息+订单中的商品信息;

【步骤】

第一步:需求分析;
第二步:添加关联关系;
第三步:编写SQL;
第四步:配置关联关系;
第五步:运行;

第一步:【需求分析】

1、查询订单详情信息即:查询订单信息+订单中的商品信息;
2、订单信息在tb_order中,订单中的商品信息在tb_item中,这两个表是通过中间表 tb_orderdetail进行关联的。
3、关联查询思路:先查询订单表,通过订单表中的id关联中间表order_id,然后查询中间表,根据中间表的item_id关联商品表的id,最后查询商品表;

【SQL查询及结果】

 

# 【需求】:查询订单号为20140921001的订单的详情信息 订单的详情信息 = 订单+商品
SELECT
	*
FROM
	tb_order tbo
INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
INNER JOIN tb_item item ON detail.item_id = item.id
WHERE
tbo.order_number = '20140921001';
第二步:添加关联关系

【修改Order】

一个订单表中关联了多个订单详情信息,所以在订单表中添加List<Orderdetail>属性:

【Order.java】

package com.itheima.sh.pojo;
import java.util.List;
/**
 * 订单表
 * 
 */
public class Order {
    private Integer id;
    private String orderNumber;
    //关联User对象
    private User user;
    //关联订单详情列表
    private List<Orderdetail> detailList;
    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;
    }

    public List<Orderdetail> getDetailList() {
        return detailList;
    }

    public void setDetailList(List<Orderdetail> detailList) {
        this.detailList = detailList;
    }

    @Override
    public String toString() {
        return "Order{" +
                "id=" + id +
                ", orderNumber='" + orderNumber + '\'' +
                ", user=" + user +
                ", detailList=" + detailList +
                '}';
    }
}

【修改Orderdetail】

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

【Orderdetail.java】

package com.itheima.sh.pojo;
public class Orderdetail {   
    private Integer id;  
    private Double totalPrice;  
    private Integer status;
    //商品信息
    private Item item;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }

    public Double getTotalPrice() {
        return totalPrice;
    }

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

    public Integer getStatus() {
        return status;
    }

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

    public Item getItem() {
        return item;
    }

    public void setItem(Item item) {
        this.item = item;
    }
    @Override
    public String toString() {
        return "Orderdetail{" +
                "id=" + id +
                ", totalPrice=" + totalPrice +
                ", status=" + status +
                ", item=" + item +
                '}';
    }
}
第三步:编写接口方法

在OrderMapper接口中新增,根据orderNumber查询订单及订单详情的方法:

public interface OrderMapper {
    /**
     * 根据orderNumber查询订单及其详情信息
     * @param orderNumber
     * @return
     */
    Order queryOrderAndDetailByOrderNumber(@Param("orderNumber") String orderNumber);
}
第四步:编写SQL

说明:一定要记住这里给order表的id起别名是oid,订单详情表的id起别名是detailId,商品表item的id起别名是itemId。在resultMap标签的id子标签中的column属性值书写对应的oid、detailId和itemId.

 <!--订单及订单详情结果集-->
    <resultMap id="orderAndDetailMap" type="Order" autoMapping="true">
        <!--tb_order表 和 Order实体类-->
        <!--订单表主键-->
        <id property="id" column="oid"/>
        <!--多个订单详情 1对多:detailList-->
        <collection property="detailList" javaType="List" ofType="Orderdetail" autoMapping="true">
            <!--tb_order_detail表  和 Orderdetail实体类-->
            <!--订单详情主键  detailId表示下面sql语句的别名-->
            <id property="id" column="detailId"/>

            <!--关联商品对象  一对一:orderdetail-Item-->
            <association property="item" javaType="Item" autoMapping="true">
                <!--tb_item表 和 Item实体类  itemId 表示下面的sql语句别名-->
                <id property="id" column="itemId"/>
            </association>
        </collection>
    </resultMap>

    <!--多对多查询-->
    <select id="queryOrderAndDetailByOrderNumber" resultMap="orderAndDetailMap">
        SELECT
            tbo.id as oid,
            tbo.order_number,
            detail.id as detailId,
            detail.total_price,
            detail.status,
            item.id as itemId,
            item.item_detail,
            item.item_name,
            item.item_price
        FROM
            tb_order tbo
            INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
            INNER JOIN tb_item item ON detail.item_id = item.id
        WHERE
            tbo.order_number = #{orderNumber};
    </select>
第五步:测试
    @Test
    public void queryOrderAndDetailByOrderNumber() {
        Order order = mapper.queryOrderAndDetailByOrderNumber("20140921001");
        System.out.println("order = " + order);
    }

【结果】

 

扩展

【需求】根据订单号(20140921001)

  • 查询订单信息
  • 查询订单所属用户信息
  • 查询订单中的详细商品信息

 

【SQL实现及查询结果】

通过分析,实现这个查询就在上面的查询基础上再关联一个一对一的User信息;

#查询订单详情
SELECT
	tbo.id as oid,
	tbo.order_number,
	detail.id as detailId,
	detail.total_price,
	detail.status,
	item.id as itemId,
	item.item_detail,
	item.item_name,
	item.item_price,
	tbu.id as uid,
	tbu.age,
	tbu.name,
	tbu.password,
	tbu.sex,
	tbu.user_name
FROM
	tb_order tbo
INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
INNER JOIN tb_item item ON detail.item_id = item.id
INNER JOIN tb_user tbu ON tbo.user_id = tbu.id
WHERE
tbo.order_number = '20140921001';

【添加关联关系】

都已经在实体类添加完毕,直接操作即可

编写接口方法

在OrderMapper接口中再扩展一个方法:queryOrderAndDetailAndUserByOrderNumber

    /**
     * 根据orderNumber查询 订单,详情,商品及用户数据
     * @param orderNumber
     * @return
     */
    Order queryOrderAndDetailAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
 <!--订单及订单详情结果集-->
    <resultMap id="orderAndDetailMapPlus" type="Order" autoMapping="true">
        <!--tb_order表 和 Order实体类-->
        <!--订单表主键-->
        <id property="id" column="oid"/>
        <!--Order-User:一对一关联-->
        <association property="user" javaType="User" autoMapping="true">
            <!--User主键-->
            <id property="id" column="uid"/>
        </association>
        <!--多个订单详情 1对多:detailList-->
        <collection property="detailList" javaType="List" ofType="Orderdetail" autoMapping="true">
            <!--tb_order_detail表  和 Orderdetail实体类-->
            <!--订单详情主键-->
            <id property="id" column="detailId"/>

            <!--关联商品对象  一对一:orderdetail-Item-->
            <association property="item" javaType="Item" autoMapping="true">
                <!--tb_item表 和 Item实体类-->
                <id property="id" column="itemId"/>
            </association>
        </collection>
    </resultMap>
    <select id="queryOrderAndDetailAndUserByOrderNumber" resultMap="orderAndDetailMapPlus">
        SELECT
            tbo.id as oid,
            tbo.order_number,
            detail.id as detailId,
            detail.total_price,
            detail.status,
            item.id as itemId,
            item.item_detail,
            item.item_name,
            item.item_price,
            tbu.id as uid,
            tbu.age,
            tbu.name,
            tbu.password,
            tbu.sex,
            tbu.user_name
        FROM
            tb_order tbo
            INNER JOIN tb_orderdetail detail ON tbo.id = detail.order_id
            INNER JOIN tb_item item ON detail.item_id = item.id
            INNER JOIN tb_user tbu ON tbo.user_id = tbu.id
        WHERE
            tbo.order_number = #{orderNumber};
    </select>
测试

结果 

5、ResultMap继承

如果两个结果集有重叠的部分,如下图所示。我们可以使用结果集继承来实现重叠的结果集的复用。

orderAndDetailAndUserMap结果集可以继承orderAndDetailMap结果集。

6.高级查询小结

resutlType无法帮助我们自动的去完成映射,所以只有使用resultMap手动的进行映射
resultMap: 
    属性:
        type 结果集对应的数据类型  Order
        id 唯一标识,被引用的时候,进行指定
        autoMapping 开启自动映射
        extends 继承
    子标签:
     id:配置id属性
     result:配置其他属性
      association:配置一对一的映射
          property 定义对象的属性名
          javaType 属性的类型
          autoMapping 开启自动映射
      collection:配置一对多的映射
          property 定义对象的属性名
          javaType 集合的类型
          ofType 集合中的元素类型 泛型
            autoMapping 开启自动映射

 

7.mybatis延迟加载

延迟加载概述
  1. 应用场景

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

  2. 延迟加载的好处

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

  3. 延迟加载的条件:

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

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

开启延迟加载

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

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

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

【示例】

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

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

如果通过订单编号查询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;
第一步:编写接口方法

在OrderMapper接口中新建:queryOrderUserLazy方法

    /**
     * 根据orderNumber懒加载查询订单及用户信息
     * @param orderNumber
     * @return
     */
    Order queryOrderUserLazy(@Param("orderNumber") String orderNumber);
第二步:编写SQL

【OrderMapper.xml】

 <!--懒加载结果集-->
    <resultMap id="orderUserLazyResultMap" type="Order">
        <id property="id" column="id"/>
        <!--
             1.column属性:延迟加载的sql语句中所需的参数,就是SELECT * FROM tb_user WHERE id = ?;
             该sql语句的?位置。这里书写 user_id因为执行  SELECT * FROM tb_order WHERE order_number = #{orderNumber} 语句查询的用户id对应的列名就是user_id

             2.select属性:调用指定sql语句来执行延迟加载,就是指定sql语句所在的位置,根据用户id查询用户信息位于UserMapper.xml文件中,具体地址是:namespace.id
             3.执行流程:
                先执行SELECT * FROM tb_order WHERE order_number = #{orderNumber} 根据订单号获取订单信息(包括用户id)
                然后根据 select="com.itheima.sh.dao.UserMapper.selectById" 找到UserMapper.xml文件中的:
                    <select id="selectById" resultType="User">
                        select * from tb_user where id = #{id}
                    </select>
              并将column="user_id" 对应的列值传递过去
              并将查询的结果封装到User对象中,最后将User对象封装到Order对象中
          -->
        <association property="user" javaType="User" column="user_id"  select="com.itheima.sh.dao.UserMapper.selectById"></association>
    </resultMap>
    <select id="queryOrderUserLazy" resultMap="orderUserLazyResultMap">
        SELECT * FROM tb_order WHERE order_number = #{orderNumber}
    </select>

【UserMapper.xml】

<mapper namespace="com.itheima.sh.dao.UserMapper">
    <!--
    查询语句
    id: 接口中方法的名字
    resultType:返回的实体类的类型,类全名
    -->
    <select id="selectById" resultType="User">
        select * from tb_user where id = #{id}
    </select>
    
</mapper>

说明:

 1.column属性:延迟加载的sql语句中所需的参数,就是SELECT * FROM tb_user WHERE id = ?;
   该sql语句的?位置。这里书写 user_id因为执行  SELECT * FROM tb_order WHERE order_number = #{orderNumber} 语句查询的用户id对应的列名就是user_id
 2.select属性:调用指定sql语句来执行延迟加载,就是指定sql语句所在的位置,根据用户id查询用户信息位于
 UserMapper.xml文件中,具体地址是:namespace.id
 3.执行流程:
   先执行SELECT * FROM tb_order WHERE order_number = #{orderNumber} 根据订单号获取订单信息(包括用户id)
   然后根据 select="com.itheima.sh.dao.UserMapper.selectById" 找到UserMapper.xml文件中的:
   <select id="selectById" resultType="User">
       select * from tb_user where id = #{id}
   </select>
  并将column="user_id" 对应的列值传递过去
 并将查询的结果封装到User对象中,最后将User对象封装到Order对象中

【注意】

  1. 懒加载首先是把SQL拆分;

  2. 然后在resultMap中,通过关联映射配置中的,select属性引入需要懒加载进来的对象;

 

第三步:开启懒加载

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

    <!--settings属性配置-->
    <settings>
        <!--开启延迟加载-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--关闭积极加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
第四步:测试

【没有访问关联对象】

在没有访问关联对象的属性时,只发送了一条SQL。

 【访问关联对象的属性】

 访问关联对象属性时,发送了两条SQL语句。按需查询-----懒加载!

8.mybatis注解开发

概述
  • 上述我们已经学习mybatis的SQL映射文件可以使用xml的方式配置,但是我们发现不同的用户模块接口都对应一个映射文件,并且在映射文件中书写sql语句也比较麻烦。所以Mybatis为用户提供了快速的开发方式,因为有时候大量的XML配置文件的编写时非常繁琐的,因此Mybatis也提供了更加简便的基于注解(Annnotation)的配置方式。
  • 注解配置的方式在很多情况下能够取代mybatis的映射文件,提高开发效率。
注解实现CRUD
  • 说明:在演示注解开发之前,为了避免和之前的映射文件混淆,所以可以将之前书写的代码放到一个新的工程中,删除映射文件即可。
CRUD相关注解

【注解】

@Insert:保存  
         Value:sql语句(和xml的配置方式一模一样)
         
@Update:更新 
         Value:sql语句
         
@Delete: 删除
         Value:sql语句
         
@Select: 查询
         Value:sql语句
         
@Options:可选配置(获取主键)
         userGeneratedKeys:开关,值为true表示可以获取主键  相当于select last_insert_id()
         keyProperty     :对象属性
         keyColumn       : 列名

【使用方式】

【第一步】将mybatis全局配置文件mybatis-config.xml中的mapper路径改为包扫描或者class路径;

说明:因为没有了映射文件,所以我们这里采用加载接口方式,需要告知mybatis哪个接口的方法上的注解需要被执行。

【第二步】编写接口和注解;

【第三步】测试

新增

目标:使用注解@Insert的方式新增数据

步骤:

第一步:UserMapper接口中新增用户方法上面编写注解;
第二步:测试

实现:

第一步:在UserMapper接口中的saveUser()方法上面添加@Insert注解,并设置该注解的value属性值为具体的SQL语句;

public interface UserMapper {
    //1、新增数据  #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
    @Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
    void saveUser(User user);
}

第二步:测试

在UserMapperTest类下面对save方法进行测试:

public class MybatisTest01 {
    private static UserMapper userMapper;
    @BeforeClass
    public static void berofeClass() throws Exception {
        //1、从xml中构建SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //这里指定了环境为test
//        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"test");
        //build不方法不指定环境就使用默认的 <environments default="development">
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //2、获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession(true);

        //3、获取UserMapper接口的动态代理对象
        userMapper = sqlSession.getMapper(UserMapper.class);
    }

    @Test
    public void saveUser(){
        User user = new User();
        user.setUserName("锁哥");
        user.setAge(18);
        user.setName("黑旋风");
        user.setPassword("1234");
        user.setSex(1);
        userMapper.saveUser(user);
    }
}

小结:

新增数据的注解为:@Insert ,作用等同于映射文件中的<insert>具体使用时,需要给其value属性设置具体的SQL。

删除

目标:使用注解@Delete删除id值为1的数据

步骤:

第一步:在根据id删除数据的方法上面编写注解@Delete;
第二步:测试

实现:

第一步:在UserMapper接口中的deleteUserById方法上编写@Delete,并设置其value属性值为具体的删除SQL;

    /*
        2.根据id删除用户
     */
    @Delete("delete from user where id=#{id}")
    void deleteUserById(Long id);

 第二步:测试

    @Test
    public void deleteUserById(){
        userMapper.deleteUserById(1L);
    }

小结:

删除数据的注解:@Delete,作用等同于映射文件中的<delete>,具体使用时,需要设置其value属性值为具体的删除SQL;

修改

目标:修改id为1的用户的数据

步骤:

第一步:在根据id修改用户数据方法上面添加注解@Update,然后在其value属性值中编写具体的SQL;
第二步:测试

实现:

第一步:在UserMapper接口的updateUser方法上添加注解:@Update,然后将其value属性值设置成update的SQL;

  /**
     * 3.修改用户数据
     * @param user
     */
    @Update("UPDATE tb_user SET user_name=#{userName}, password=#{password} ,name=#{name} ,age=#{age},sex=#{sex} where id=#{id}")
    void updateUser(User user);

 第二步:测试

    @Test
    public void updateUser(){
        User user = new User();
        user.setId(1L);
        user.setUserName("柳岩");
        user.setSex(0);
        user.setPassword("3456");
        user.setName("岩岩");
        user.setAge(20);
        userMapper.updateUser(user);
    }

小结:修改数据的注解:@Update,作用等同于映射文件中的<update>

查询

目标:使用注解查询所有的用户数据

步骤:

第一步:在接口中查询所有的用户数据的方法上面添加注解:@Select,然后设置其value属性值为具体的SQL查询语句;
第二步:测试

实现:

第一步:在UserMapper接口的queryAllUsers方法上添加注解:@Select,然后设置其value属性值为具体的查询SQL;

  /*
     * 4.查询所有用户数据
     */
    @Select("SELECT * FROM tb_user")
    List<User> queryAllUsers();

 第二步:测试

    @Test
    public void queryAllUsers(){
        List<User> list = userMapper.queryAllUsers();
        System.out.println("list = " + list);
    }

小结:

查询数据注解:@Select ,作用等同于映射文件中的<select>标签。

返回新增数据的id(自增主键回填)

问题:上面注解实现CRUD的测试中,数据新增成功,但是id值没有正常返回.

 

目标:使用注解完成数据新增,新增成功后返回数据的主键id值

步骤:

1、在新增数据注解 @Insert下面,添加@Options;
2、在Options注解中,设置useGeneratedKeys值为true,keyProperty为id,keyColumn id;

实现:

第一步:在新增数据注解 @Insert下面,添加@Options,设置useGeneratedKeys值为true,keyProperty为id,keyColumn 为id;

    //1、新增数据  #{userName} 这里的userName是方法saveUser(User user)参数User类的成员变量
    @Insert("INSERT INTO tb_user VALUES(NULL,#{userName},#{password},#{name},#{age},#{sex})")
    @Options(useGeneratedKeys = true,keyColumn = "id",keyProperty = "id")
    void saveUser(User user);

 第二步:测试:

小结:注解@Options

 

注解实现别名映射

根据之前的学习,如果数据表的列名和pojo实体类的属性名不一致,会导致数据表的数据无法封装到实体类属性值中,对此我们又如下解决方案:

【1】查询的时候给列起别名,别名和实体类的属性名一致

select user_name as userName from tb_user where id =1;

【2】在mybatis的核心配置文件中按照如下配置:

<settings>
    <!--开启驼峰自动映射-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

【3】在映射文件中,我们可以通过在ResultMap中,通过result标签中,给指定的column映射到实体类中指定的属性上。

 <resultMap id="orderAndUserResultRelative" type="Order">
     <result column="user_name" property="userName"/>
 </resultMap>

而在注解中也有相应的解决方案:这里就必须使用注解:@Results

@Results注解相当于之前映射文件中的ResultMap,该注解如下:

public @interface Results {
    Result[] value() default {};
}

我们发现value属于Result数组类型,而Result属于一个注解,注解的属性如下:

public @interface Result {
    //对应数据表的列
    String column() default "";
	//对应pojo类的属性
    String property() default "";
	//javaType:返回的对象类型
    Class<?> javaType() default void.class;
	//one: 一对一配置
    One one() default @One;
	//many: 一对多配置
    Many many() default @Many;
}

目标:使用注解的方式给取别名后的字段,映射到实体类中

步骤:

第一步:将之前核心配置文件中的开启驼峰自动映射设置为false
 <settings>
        <!--开启驼峰自动映射-->
        <setting name="mapUnderscoreToCamelCase" value="false"/>
 </settings>
第二步:在接口中查询的方法上面添加注解@Results;
第三步:测试;

实现:

第一步:将之前核心配置文件中的开启驼峰自动映射设置为false。这样才可以演示出@Results的效果。

 <settings>
        <!--开启驼峰自动映射-->
        <setting name="mapUnderscoreToCamelCase" value="false"/>
 </settings>

第二步:在接口中查询方法上面添加注解:@Results ,然后通过@Result完成字段的别名和实体类的属性名之间的映射配置;

说明:这里我们使用之前的查询所用用户方法即可,也可以在接口中在定义一个根据id查询用户的方法。

	/*
        根据id查询用户
     */
    @Select("SELECT * FROM tb_user WHERE id = #{id}")
    @Results(value={
            @Result(column = "user_name",property = "userName")
    })
    User selectById(@Param("id") Long id);

第三步:测试

   @Test
    public void selectById(){
        User user = userMapper.selectById(1L);
        System.out.println("user = " + user);
    }

【结果】

小结:给别名映射到实体类中可以通过添加注解:@Results 中的 @Result实现;

9.总结

一、全局配置:能够影响mybatis的运行
    【1】能够配置的参数:
            properties:
            settings:
            typeAliases:
            mappers:
    【2】properties:属性
        作用:设置参数 ,加载外部的资源文件中的参数
        用法:
            1、设置参数:
                <properties>
                    <property name="driver" value="com.mysql.jdbc.Driver"></property>
                </properties>
               取参数: ${driver}
            2、加载外部的资源文件:从src路径下加载
                <properties resource="jdbc.properties"></properties>
                获取资源文件中的参数: ${key}

    【3】settings设置:
            开启驼峰自动映射:
                <settings>
                          <setting name="mapUnderscoreToCamelCase" value="true"/>
                </settings>
            作用: 数据库下划线命名: user_name    驼峰命名:userName
    【4】typeAliases:类型别名 
            作用:给全限定类名 取一个 短名称  com.heima.mybatis.pojo.User ==> User
            用法:
                1、单独设置:
                    <typeAliases>
                          <typeAliase type="com.heima.mybatis.pojo.User" alias="User"></typeAliase>
                    </typeAliases>

                2、批量设置:包扫描
                    <typeAliases>
                          <package name="com.heima.mybatis.pojo"></package>
                      </typeAliases>
    【5】mappers 映射器:
        【作用】管理映射文件 和  接口
        【配置】
            1、加载xml文件
                 从src下加载:resource="UserMapper.xml"
                 从本地磁盘: url="file:///E://aaa.xml"

            2、加载接口: 接口和映射文件同名,在相同的包下面
                 单独加载: class="com.heima.mybatis.dao.UserMapper"
                 批量加载: package="com.heima.mybatis.dao"


二、映射文件配置
    【1】作用:配置sql相关信息 
             1、CRUD四类标签:
             2、sql标签:sql片段
             3、resultMap:
    【2】CRUD四类标签:
            增: <insert></insert>
            删: <delete></delete>
            改:<update></update>
            查:<select></select>

    【3】入参相关:<select resultType="User" paramType="int">
                      SELECT * FROM user WHERE id = #{id}
                </select>
            
        paramType:设置传入的参数的类型,可以省略
        insert语句相关:自增主键回填--数据插入到数据库之后,返回这条数据在数据库中的主键值
            【1】子标签:<selectKey></selectKey>
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            SELECT LAST_INSERT_ID() ;
        </selectKey>
            【2】属性:
                useGeneratedKeys="true" keyColumn="id" keyProperty="id" 


    【4】sql参数传入:
            能够接收的参数类型: 基本类型数据,pojo,map

    【5】传入基本类型数据:string,int
         单个数据传入: queryById(Integer id)        WHERE id = #{id}
        多个数据传入:queryByNameAndSex(String name,String sex):  
                        WHERE username=#{} and sex= #{}
        最佳实践:命名参数取值  
                queryByNameAndSex(@Param("name")String name,@Param("sex")String sex):  
                WHERE username=#{name} and sex= #{sex}

    【6】传入pojo和map数据:
            pojo: User{id,username}   #{username}
            map:  Map<Key,Value>      #{key}
    
     【7】映射文件中sql语句取值: #{}  ${}
              相同点:都能够获取命名参数值
              不同点:
                      #{} 取值使用预编译方式设置参数
                      ${} 直接标签获取的参数拼接到sql语句中
           最佳实践: 获取查询参数,防止SQL注入 使用  #{}取值
              
      【8】${}应用场景: 拼接SQL语句
       
       
       【9】sql返回值处理:
              resultType:基本类型数据,pojo,map
                  基本类型数据: resultType="int"
                  pojo类型:
                      单个pojo,多个pojo:   resultType="pojo"
                  map:
                      单条数据:map  默认    resulteType="map"
                      多条数据:map    @MapKey("id")
             
              resultMap:
                【1】设置数据库的列名  和  实体类的属性名之间  映射关系
                   【2】多表关联配置:
              
        【10】SQL片段: <sql>id,username</sql>
         【11】动态sql:
                  判断: if,分支
                  trim: where,set
                  foreach:遍历

  • 8
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿瞒有我良计15

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

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

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

打赏作者

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

抵扣说明:

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

余额充值