MyBatis(第2天)-xmlMyBatis增删改查&传统Dao实现类开发方式&动态SQL环境搭建与编写


MyBatis(第2天)-xmlMyBatis增删改查&传统Dao实现类开发方式&动态SQL环境搭建与编写

回顾

  1. mybatis的配置文件有哪两种?

    核心配置文件:里面配置数据库的连接信息

    接口映射文件:让接口中的方法对应SQL语句

  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.itheima.dao.UserMapper">
    <select id="findAllUsers" resultType="com.itheima.entity.User">
        select * from User
    </select>
</mapper>

namespace: 包名.接口名
id: 接口中的方法名
resultType: 方法的返回值类型
中间内容: SQL语句
  1. 说说以下各个类的作用:
类名作用
SqlSessionFactoryBuilderSqlSessionFactory建造者,作用创建SqlSessionFactory
SqlSessionFactorySqlSessionFactory工厂类,作用创建SqlSession
SqlSessionSqlSession SQL会话类,相当于数据库连接

学习目标

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

项目结构

  1. mybatis_day02_01_xml用户的增删改查操作

  2. mybatis_day02_02_old传统DAO实现类方式

  3. mybatis_day02_03_dynamic_sql动态SQL

mybatis框架的学习,不要纠结,老想为什么,我们的目标使用mybatis,等我们框架使用熟练后,看源码学习。

三种开发方式

  1. mapper接口代理的开发方式(重点)
  2. 传统DAO实现类的开发方式(目前基本淘汰)
  3. 使用注解的开发方式

接口代理方式:搭建项目环境

目标

搭建Mybatis项目环境

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jYXLjqY-1598011240779)(/1562577845493.png)]

数据库SQL语句
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','女','盤丝洞');
实现步骤
  1. 创建模块,加入依赖包
  2. 复制核心配置文件sqlMapConfig.xml和log4j.properties到src下
创建模块,加入依赖包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZTbAAIs-1598011240784)(/1562579110663.png)]

准备核心配置文件

sqlMapConfig.xml和log4j.properties

<?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>
    <!--可以配置多个环境,可以访问不同种类的数据库:mysql, oracle-->
    <environments default="default">
        <!-- 其中的一个配置环境,这个配置方案的唯一标识 -->
        <environment id="default">
            <!--
            指定事务管理器的类型:
            JDBC:使用JDBC来管理事务
            -->
            <transactionManager type="JDBC"/>
            <!--
            数据源的类型:
            1. POOLED:使用mybatis创建的连接池
            2. UNPOOLED:不使用连接池,每次都创建和关闭连接
            -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!--加载接口映射文件-->
    <mappers>
        
    </mappers>
</configuration>

接口代理方式:根据用户ID查询用户

目标

根据用户 ID 查询用户

步骤分析
  1. 编写用户User的实体类
  2. 编写用户UserMapper接口
  3. 配置接口映射文件UserMapper.xml
  4. 使用SqlSession来执行Mapper的方法
编写用户User的实体类
package com.itheima.entity;

import java.sql.Date;

/**
 用户实体类对象 */
public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

   // 省略构造方法/getter/setter/toString
}
编写用户UserMapper接口
public interface UserMapper {
    /*
    通过id查询到一个用户
    */
    User findUserById(Integer id);
}
创建UserMapper.xml文件

在com.itheima.dao包下创建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.itheima.dao.UserMapper">
    <!--parameterType:方法参数的类型-->
    <!--resultType:方法返回值类型-->
    <!--#{}:相当于?占位符,#{id}:得到方法的id参数放在这个位置-->
    <select id="findUserById" parameterType="java.lang.Integer" resultType="com.itheima.entity.User">
        SELECT * FROM USER WHERE id=#{id};
    </select>
</mapper>

在mapper中配置上面的UserMapper.xml文件

<!--配置接口映射文件-->
<mappers>
    <mapper resource="com/itheima/dao/UserMapper.xml"/>
</mappers>
测试
public class TestUserMapper {
    @Test
    public void test01() throws IOException {
        InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory sessionFactory = builder.build(in);
        SqlSession sqlSession = sessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User userById = mapper.findUserById(2);
        System.out.println("userById = " + userById);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l9XIfp2N-1598011240799)(/1562579725880.png)]

小结

  1. Mybatis如何取到方法的参数放到sql中

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TbylP8b6-1598011240804)(/1578877776344.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-llDvLhkA-1598011240807)(/1578877785992.png)]

#{xx}: 先使用?占位, 后续将具体值赋值给?

接口代理方式:修改数据

目标

通过id修改用户数据

实现步骤
  1. 在接口中编写一个修改用户的方法
  2. 在接口映射文件中编写相应的SQL语句
  3. 测试
在接口中编写一个修改用户的方法
/**
 * 根据用户Id修改用户
 */
void updateUser(User user);
在接口映射文件中编写相应的SQL语句

使用update标签:放置修改sql语句,根据用户id修改用户其它属性

<!--
update标签更新记录
id: 方法名字
parameterType: 参数的类型
-->
<update id="updateUser" parameterType="com.itheima.entity.User">
    UPDATE user set username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} where id=#{id};
</update>
测试
  1. 修改6号用户的名字,生日,性别,地址
  2. 更新用户对象
// 更新记录
@Test
public void testUpdateUser() {
    User user = new User(6, "狐狸精",Date.valueOf("1996-05-10"),"女","狐狸洞");
    userMapper.updateUser(user);
}
  • 事务的处理:如果Java程序代码执行成功,但是数据库中并没有新增记录。原因是没有提交事务,在对数据库的更新操作中(增、删、改)要求提交事务。
提交事务
方式一:手动提交事务
sqlSession.commit();  // 方式一:手动提交事务
方式二:自动提交事务
sqlSession = factory.openSession(true);  // 方式二:自动提交

说明:如果在同一个方法中,有多个数据库操作,需要使用手动提交的方式。

如果SQL语句每条都是独立的,使用自动提交

如果是多条SQL语句组成一个功能,手动提交

小结

  1. 参数是实体对象的时候,占位符#{}中的变量名是什么?

    #{成员变量名}: 取出这个对象的指定成员变量

  2. 开启事务有哪两种方式?

    手动提交: sqlsession.commit();

    自动提交:sqlSessionFactory.openSession(true);

接口代理方式:删除数据

目标

根据id删除数据

效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OBIrS7x1-1598011240811)(1558087744181.png)]

实现步骤
  1. 在接口中编写一个删除用户的方法
  2. 在接口映射文件中编写相应的SQL语句
  3. 测试
在接口中编写一个删除用户的方法
/**
 * 根据用户id删除用户
 */
void deleteUser(int id);
在接口映射文件中编写相应的SQL语句

delete标签:放置删除sql语句,根据用户Id删除用户

<!--
delete标签用于删除记录
-->
<delete id="deleteUser" parameterType="int">
     DELETE from user where id=#{id}
</delete>
测试

删除11号用户

// 删除记录
@Test
public void testDeleteUser() {
    userMapper.deleteUser(11);
}

小结

删除数据的步骤:

  1. 在接口中编写删除的方法
  2. 在接口映射文件中编写删除的SQL语句
  3. 测试

接口代理方式:新增用户

目标

  1. 添加用户
效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j496csPp-1598011240815)(1558085199529.png)]

实现步骤
  1. 在mapper接口定义新增用户方法
  2. 配置mapper映射文件SQL语句
  3. 测试
在mapper接口定义新增用户方法
/**
 * 新增用户
 */
void addUser(User user);
配置mapper映射文件
  1. 新增用户使用insert标签
  2. 放置新增sql语句,参数类型使用User
  3. 占位符使用user对象的各个#{属性名}
<!--
insert标签:表示添加记录
id: 方法名
如果参数是实体类,#{属性名}
-->
<insert id="addUser" parameterType="com.itheima.entity.User">
   INSERT INTO user VALUES (null, #{username}, #{birthday},#{sex},#{address})
</insert>
测试
new User(null,"白龙马",Date.valueOf("2019-05-01"),"男","东海龙宫");

查询新增记录的主键值

当数据库表中,主键字段值由数据库维护(比如mysql中的自增长),那么在新增完一条记录以后,如何获取到数据库维护的主键值呢?

目标

通过子元素<selectKey>得到新增记录的主键值

通过属性useGeneratedKeys得到新增记录的主键值

需求效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I9Dd6iW3-1598011240817)(1558086213536.png)]

方式一:子元素<selectKey>(常用)

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

原理:在insert语句执行后再执行一条查询语句,返回新增主键的id

属性说明
keyColumn主键在表中的字段:user表中id列
keyProperty实体类中主键的属性名 User中id
resultType主键类型,int类型
orderBEFORE: 在insert之前执行
AFTER: 在insert之后执行
<!--
insert标签:表示添加记录
id: 方法名
如果参数是实体类,#{属性名}
-->
<insert id="addUser2" parameterType="com.itheima.entity.User">
    <!--
     keyColumn  主键在表中的字段:user表中id列
     keyProperty 实体类中主键的属性名 User中id
     resultType 主键类型,int类型
     order
        BEFORE: 在insert之前执行
        AFTER: 在insert之后执行
     -->
    <selectKey keyColumn="id" keyProperty="id" resultType="int" order='after'>
        SELECT LAST_INSERT_ID();
    </selectKey>
   INSERT INTO user VALUES (null, #{username}, #{birthday}, #{sex}, #{address})
</insert>

测试代码

// 添加记录
@Test
public void testAddUser() {
    // 在mybatis中默认是手动提交事务,增删改要提交事务
    // Date.valueOf(字符串),将字符串转成日期,格式一定要是yyyy-MM-dd
    User user = new User(null,"白龙马", Date.valueOf("2019-05-01"),"男","东海龙宫");
    int row = userMapper.addUser(user);
    System.out.println("user = " + user);
}
方式二:在insert标签中增加属性
属性说明
useGeneratedKeystrue,使用mysql生成的主键
keyColumn表中主键对应的字段
keyProperty实体类中对应的属性

映射文件

<!--
insert标签:表示添加记录
id: 方法名
如果参数是实体类,#{属性名}
-->
<insert id="addUser" parameterType="com.itheima.entity.User" keyColumn="id" keyProperty="id" useGeneratedKeys="true">
   INSERT INTO user VALUES (null, #{username}, #{birthday},#{sex},#{address})
</insert>
  • 说明:直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库,比如MySQL或SQL Server。

小结

  1. 插入数据后得到主键值有哪两种方式?

    <!--insert: 表示添加数据-->
    <insert id="addUser" parameterType="com.itheima.entity.User">
        INSERT INTO user VALUES (null, #{username}, #{birthday}, #{sex}, #{address});
        <!--selectKey:获取插入数据的自增主键
            keyColumn: 数据库表中的主键字段
            keyProperty: 类中存放主键的成员变量
            resultType: 表中主键的类型
            order: 执行的时机:  BEFORE: 在执行SQL语句前查询主键 AFTER在执行SQL语句后查询主键
        -->
        <selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
            SELECT LAST_INSERT_ID();
        </selectKey>
    </insert>
    
    <!--insert:
    useGeneratedKeys: 获取插入数据的自增主键
    keyColumn: 数据库表中的主键字段
    keyProperty: 类中存放主键的成员变量
    -->
    <insert id="addUser2" parameterType="com.itheima.entity.User" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        INSERT INTO user VALUES (null, #{username}, #{birthday}, #{sex}, #{address});
    </insert>
    

核心配置文件:properties标签

目前我们将数据库相关的信息都是配置在核心配置文件中,如果核心配置文件中的内容很多,那么修改的时候就不是很方便。我们可以将数据库的配置信息专门放到一个单独的文件中,使用properties标签引入即可。

目标

学习properties标签的作用

sqlMapConfig.xml元素概述

sqlMapConfig.xml是mybatis框架的核心配置文件,目前我们在其中配置了运行环境(数据源)和加载映射文件。该配置文件中还有其它的一些配置。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCl0MZcV-1598011240818)(1558074473713.png)]

properties的作用

将外面的属性文件(.properties)加载进来。在后面就可以引用属性文件中的键和值

操作步骤

编写数据库连接属性资源文件(jdbc.properties)

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

在核心配置文件中通过properties标签加载jdbc.properties属性资源文件

<!--
属性:
resource: 指定类路径下Java的属性文件
如果内部和外部有相同的键,使用外部的
-->
<properties resource="db.properties">
    <property name="jdbc.password" value="root"/>
</properties>

<environments default="mysql">
    <environment id="mysql">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <!--1.3配置连接池需要的参数-->
            <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>

小结

  1. properties标签作用是什么?

    可以加载外部的properties文件的数据.

  2. 内部配置的property和外面配置的properties键如果同名,使用哪个?

    使用外部的

核心配置文件:typeAliases别名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5osazS9A-1598011240820)(/1562635698045.png)]

我们在接口映射文件中指定参数或返回值类型时,需要写出具体的包名和类名,是比较麻烦的。我们可以通过别名更简单,比如中华人民共和国,简称中国。

目标

学习typeAliases标签的使用

typeAliases作用

给自定义类取别名,让类使用更简单

内置别名
别名映射类型
stringjava.lang.String
bytejava.lang.Byte
longjava.lang.Long
shortjava.lang.Short
intjava.lang.Integer
integerjava.lang.Integer
doublejava.lang.Double
floatjava.lang.Float
booleanjava.lang.Boolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

说明:

  • 内置别名可以直接使用,Mybatis内置别名的规律:别名就是类名的小写

  • 使用别名时不区分大小写: object, OBJECT, obJEct

自定义类取别名方式一:typeAlias
  1. 在配置文件中将用户指定别名为user
  2. 将结果类型设置为user
  3. 省略alias的情况
<typeAlias type="com.itheima.entity.User" alias="User"/>
<typeAlias type="com.itheima.entity.User"/>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WSFo2HWV-1598011240822)(/1565057764936.png)]

自定义类取别名方式二:包扫描配置别名package

如果有很多类,那么都需要取别名,一个一个取别名太麻烦了。可以使用包扫描方式。

 <!--
    定义别名
    typeAlias 子元素
        type: 类全名
        alias: 别名,可以省略。默认使用类名做为别名,不区分大小写
    package子元素:
        给包里面所有类取别名, 别名就是类名,不区分大小写
 -->
 <typeAliases>
     <typeAlias type="com.itheima.entity.User" alias="user"/>
     <package name="com.itheima.entity"/>
</typeAliases>

小结

typeAlias标签的作用:给类取别名

<!--typeAlias: 给类取别名
type: 包名.类名
alias: 别名 (可以省略,使用类名作为别名)
-->
<typeAlias type="com.itheima.entity.User" alias="user"/>

package标签的作用:包扫描,给这个包中的所有类取别名,别名就是类名

<package name="com.itheima.entity"/>

核心配置文件:mappers(映射器)

目标

学习mappers标签的使用

作用

作用:加载接口映射文件

方式一:加载单个映射文件

mapper标签的属性

<!--映射器-->
<mappers>
    <!--
    resource: 指定类路径下映射文件,注:路径使用/做为分隔符,而不是点号
    class: 指定使用注解的接口名字
     -->
    <mapper resource="mapper/UserMapper.xml"/>
</mappers>

注:如果是多级目录,是/而不是点号

方式二:包扫描加载多个映射文件

包扫描方式加载mapper映射文件

  1. 要求接口映射文件,与接口要放在同一个目录

  2. 要求接口映射文件的名称,与接口的名称要一致

<!--配置接口映射文件-->
<mappers>
    <!--
    package标签:
        接口映射文件与接口文件在同一个目录
        接口映射文件的名称与接口文件的名称相同
    -->
    <package name="com.itheima.dao"/>
</mappers>

小结

mappers元素作用?

​ 指定接口映射文件的位置

mybatis模糊查询

目标

使用接口代理方式模糊查询用户

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JvI2tQV2-1598011240825)(1558083007933.png)]

实现步骤
  1. 在接口中编写一个模糊查询的方法
  2. 在接口映射文件中编写相应的SQL语句
  3. 测试

方式一

在接口中编写一个模糊查询的方法
/**
 通过用户名模糊查询用户
*/
List<User> findUsersByName(String username);
SQL语句
SELECT * FROM user WHERE username LIKE '%精%';
在接口映射文件中编写相应的SQL语句
<select id="findUserByname" parameterType="string" resultType="user">
    SELECT * FROM user WHERE username LIKE #{username};
</select>
测试

查询名字中包含"精"字的用户

// 按名字模糊查询用户
@Test
public void testFindUsersByName() {
    List<User> list = mapper.findUserByname("%精%");
    for (User user : userList) {
        System.out.println(user);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bXziCPrI-1598011240826)(/1562658586920.png)]

方式二

在接口映射文件中编写相应的SQL语句
<select id="findUserByname" parameterType="string" resultType="user">
    SELECT * FROM user WHERE username LIKE '%${value}%';
</select>
测试2

查询名字中包含"精"字的用户

// 按名字模糊查询用户
@Test
public void testFindUsersByName() {
    List<User> list = mapper.findUserByname("精");
    for (User user : userList) {
        System.out.println(user);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n9q721FF-1598011240827)(/1562658548824.png)]

小结

#{} 先使用?占位,后面给?赋值

​ 当参数是基本数据类型或者包装类或者String

​ #{随便写}

​ 当参数是自定义类型User

​ #{类中的成员变量名}

${} 字符串拼接,拿到参数和sql拼接(不建议使用)

​ 当参数是基本数据类型或者包装类或者String

​ ${value}

​ 当参数是自定义类型User

​ ${类中的成员变量名}

接口映射文件中方法的的参数类型可以省略

方法的返回值类型不能省略

接口映射文件:三种参数类型

目标

  1. Java简单类型

  2. POJO类型

  3. POJO包装类型

简单类型

什么是简单类型:基本数据类型的8种+String,包括对应的包装类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z12m31Ru-1598011240829)(1558087991710.png)]

POJO类型

POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBean,即我们前面封装数据使用的实体类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UVmtdv2R-1598011240830)(1558088005032.png)]

POJO包装类型

什么是POJO包装类型:就是在实体类中包含了其它的实体类

public class QueryVo {
    private User user;
    private String start;
    private String end;
}

案例: POJO包装类型的演示

目标

实现多条件查询,出生日期在start属性和end属性之间

方法定义多个参数方式实现实现多条件查询

SELECT * FROM user WHERE birthday BETWEEN1 AND2;

接口

List<User> findUsersByBirthday(String start, String end);

接口映射文件

<select id="findUsersByBirthday" parameterType="string" resultType="user">
    SELECT * FROM user WHERE birthday BETWEEN #{arg0} AND #{arg1};
</select>

测试

@Test
public void test10() throws IOException {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<User> list = mapper.findUsersByBirthday("1990-01-01", "2018-01-01");
    for (User user : list) {
        System.out.println("user = " + user);
    }

    sqlSession.close();
}

存在的问题,SQL语句可读性太差

POJO包装类型实现多条件查询

编写POJO包装类型

VO: Value Object 值对象,实体类

package com.itheima.entity;

/**
 包装类
 */
public class QueryVo {
    private User user;  // 包含用户对象
    private String start; // 开始日期
    private String end; // 结束日期

	// 省略getter/setter
}
声明mapper接口方法
/**
 * 使用POJO包装类型,根据用户名称,开始和结束生日,模糊查询用户
 */
List<User> findUsersByCondition(QueryVo queryVo);
配置mapper映射文件
<!--
queryVo:要定义别名
占位符,拼接符要使用实体类的属性名
-->
<select id="findUsersByCondition" parameterType="queryvo" resultType="user">
    SELECT * FROM user WHERE id < #{user.id} AND birthday BETWEEN #{start} AND #{end};
</select>
测试
@Test
public void testFindUsersByCondition() {
	UserMapper mapper = sqlSession.getMapper(UserMapper.class);
	QueryVo vo = new QueryVo();
	User u = new User(10, "老宋", Date.valueOf("2000-02-02"), "男", "东莞");
	vo.setUser(u);
	vo.setStart("1990-01-01");
	vo.setEnd("2018-01-01");
	List<User> list = mapper.findUsersByCondition(vo);
	for (User user : list) {
	    System.out.println("user = " + user);
	}

	// 手动提交事务
	sqlSession.commit();
	sqlSession.close();
}

小结

三种参数输入类型分别是:

  1. 简单类型

    基本数据类型8种+包装类+String
    
  2. POJO类型

    普通的JavaBena,实体类
    
    public class User {
        private Integer id;
        private String username;
        private Date birthday;
        private String sex;
        private String address;
        // 省略get/set
    }
    
  3. POJO包装类型

    public class QueryVo {
        private User user;  // 包含用户对象
        private String start; // 开始日期
        private String end; // 结束日期
    
    	// 省略getter/setter
    }
    

接口映射文件:resultType输出类型

目标

学习输出结果resultType的两种类型

  1. 简单类型
  2. POJO类型
resultType的两种类型

java简单类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oETJyS8h-1598011240832)(/1558089195041.png)]

POJO类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kuxvgkjl-1598011240833)(/1558089195040.png)]

java简单类型练习

统计用户表中女生的数量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxlGbQBx-1598011240836)(1558089051005.png)]

声明mapper接口方法

/**
 * 统计用户表中某种性别的数量
 */
int getAmountBySex(String sex);

配置mapper映射文件

<!-- 统计用户表中的女生的用户数量 -->
<select id="getAmountBySex" resultType="int" parameterType="string">
    select count(*) from user where sex=#{sex}
</select>

测试

// 查询有多少个女生
@Test
public void testGetAmountBySex() {
    int amount = userMapper.getAmountBySex("女");
    System.out.println("女生数量是:" + amount);
}

小结

resultType的输出类型有哪两种?

Java简单类型:基本数据类型8种+包装类+String

POJO: 简单的Java对象

接口映射文件:resultMap输出映射(重点)

mybatis可以把查询的结果自动封装为对象。但是有要求:数据库中的列名称,要与对象的属性一致。否则不能正确封装数据。

当查询的列与对象属性不一致时候,使用resultMap解决。resultMap可以建立查询的列与对象属性的对应关系。

SELECT id id2, username username2, birthday birthday2, sex, address FROM user WHERE id = 2;

查询一张表时,查询的字段名字和类的中的成员变量不一样,设置为null,一样的就有值

目标

使用resultMap对查询结果进行映射

配置mapper映射文件
  1. 定义resultMap标签
  2. id标签:映射主键字段,如果列名与属性名相同可以省略
  3. result标签:映射普通字段,指定哪个属性对应哪个列
  4. 在查询的结果中使用resultMap
<!--
   定义结果映射
       id: 映射的唯一标识
       type: 最终结果转换后的类型

   子元素:
       id: 定义主键字段的映射
       result: 定义普通的字段的映射
           属性:property: 实体类中属性名,column: 表中列名,如果相同可以不写。
->
<resultMap id="u1" type="user">
    <id column="id2" property="id"/>
    <result column="username2" property="username"/>
    <result column="birthday2" property="birthday"/>
</resultMap>
<select id="findUserByIdUseResultMap" parameterType="int" resultMap="u1">
    SELECT id id2, username username2, birthday birthday2, sex, address FROM user WHERE id = #{id};
</select>

测试,查询id为1的用户

@Test
public void test09() throws IOException {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.findUserByIdUseResultMap(2);
    System.out.println("user = " + user);
    // 手动提交事务
    sqlSession.close();
}
映射流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WMOZj9yS-1598011240838)(/1565075076618.png)]

映射文件的标签小结

<select> 执行查询的SQL语句
<insert> 执行插入的SQL语句
<selectKey> 获取插入数据的自增主键
<update> 执行修改的SQL语句
<delete> 执行删除的SQL语句
<resultMap> 对查询结果进行映射成对象

传统的DAO实现类的开发方式[了解]

目标

使用传统的DAO实现类开发方式

  1. 查询指定id的用户
复制新的模块
  1. 在 windows 下将 mybatis-day02-01-xml 复制成 mybatis-day02-02-old
  2. 在 windows 下将 mybatis-day02-01-xml.iml 的文件名改成 mybatis-day02-02-old.iml
  3. 导入mybatis-day02-02-old项目
编写UserMapper接口
public interface UserMapper {
    /*
    通过id查询到一个用户
    */
    User findUserById(Integer id);
}
编写UserMapper接口映射文件
<?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.itheima.dao.UserMapper">
    <select id="findUserById" parameterType="int" resultType="uSer">
        SELECT * FROM USER WHERE id=#{id};
    </select>
</mapper>
编写接口实现类UserMapperImpl
package com.itheima.dao;

import com.itheima.entity.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;

public class UserMapperImpl implements UserMapper {
    @Override
    public User findUserById(Integer id) {
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        factory = builder.build(UserMapperImpl.class.getResourceAsStream("/sqlMapConfig"));
        SqlSession session = factory.openSession();
        User user = session.selectOne("com.itheima.dao.UserMapper.findUserById", id);
        session.close();
        return user;
    }
}
编写测试类
public class TestUserMapper {
    @Test
    public void test01() {
        UserMapperImpl dao = new UserMapperImpl();

        User user = dao.findUserById(7);
        System.out.println("user = " + user);
    }
}

小结

传统的DAO实现类的开发方式和接口映射的方式区别?

传统的DAO实现类的开发方式: 需要我们自己手动写接口的实现类,并且找到相应的SQL语句去执行

接口映射的方式: sqlSession.getMapper(UserMapper.class); 通过getMapper得到接口的实现类,他会自动找到SQL语句去执行

动态SQL的概念和环境搭建

之前我们编写SQL语句的时候都是将SQL语句固定写好的

SELECT * FROM product WHERE brand='小米' AND type='ai' AND size=55;

目标

学习动态SQL的概念

搭建动态SQL的环境

什么是动态SQL

动态SQL指的是:在程序运行时,根据不同的情况,拼接最终执行的sql语句。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qta4Xke0-1598011240840)(/1562723542603.png)]

搭建动态SQL的环境

模块名:mybatis_day02_03_dynamic_sql

动态SQL:if标签

目标

根据用户名和性别查询用户

学习动态SQL语句:if标签的使用

if标签的格式
<if test="条件">
  SQL语句
</if>
if标签的作用

做判断,如果满足条件就会拼接SQL

需求实现

定义QueryVo实体类

public class QueryVo {
    private String username;
    private String sex;
    
    // 省略构造方法/getter/setter
}

声明UserMapper接口方法

package com.itheima.dao;

import com.itheima.entity.User;
import java.util.List;

public interface UserMapper {
    /*
     根据用户名称和性别查询用户
     */
    List<User> findUserByNameAndSex(QueryVo vo);
}

配置mappr映射文件

<?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.itheima.dao.UserMapper">
    <select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
        SELECT * FROM user WHERE username LIKE #{username} AND sex=#{sex};
    </select>
</mapper>

UserMapper.xml

  1. if:判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
  2. if:判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
<?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.itheima.dao.UserMapper">
    <!--直接根据用户名和性别查询-->
<!--    <select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
        SELECT * FROM user WHERE username LIKE #{username} AND sex=#{sex};
    </select>-->
    
    <!--
        判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
        判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
    -->
    <select id="findUserByNameAndSex" parameterType="User" resultType="User">
        select * from user where
        <if test="username!=null and username!=''">
          username like #{username}
        </if>
        <!-- &&表示与操作,要转义 -->
        <if test="sex!=null &amp;&amp; sex!=''">
          and sex = #{sex}
        </if>
    </select>
</mapper>

测试

  1. 通过用户名和性别查询多个用户
  2. 只设置性别
  3. 名字和性别一个都不设置
@Test
public void testFindUserBySex() {
    List<User> list = userMapper.findUserByNameAndSex(new QueryVo("", ""));
    for (User user : list) {
        System.out.println("user = " + user);
    }
}

疑问:if标签如果第1个条件没有,会出现什么情况?如何解决这个问题?

因为SQL语句拼接语句不正确,出现问题。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o1A42hZ0-1598011240842)(/1564450528189.png)]

小结

if标签的格式:

<if test="条件">
    SQL片段
</if>

if标签作用

当满足条件就会拼接SQL片段

动态SQL:where标签的作用

目标

学习where标签的使用

where标签作用
  1. 相当于where关键字,自动补全where这个关键字
  2. 去掉多余的and和or关键字
需求实现
UserMapper.xml

if标签写在where标签内部

  1. if:判断用户名称不为空,且不为空字符串,则用户名称作为查询条件
  2. if:判断用户性别不为空,且不为空字符串,则用户性别作为查询条件
<select id="findUserByNameAndSex" parameterType="queryvo" resultType="User">
    SELECT * FROM user
    <where>
        <if test="username != null and username != ''">
            username LIKE #{username}
        </if>

        <!--&&表示与操作,要转义-->
        <if test="sex != null &amp;&amp; sex != ''">
            AND sex=#{sex};
        </if>
    </where>
</select>

小结

where标签的作用

where相当于where关键字,需要就加,不需要就不加, 并且会去除掉多余的and or

动态SQL:set标签

目标

set标签的使用

编写修改SQL语句存在的问题

之前我们编写修改的SQL语句时是根据用户ID更新用户所有字段的数据

<!--根据用户ID更新用户所有字段的数据, 这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新-->
<update id="updateUser" parameterType="user">
	UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address}
	WHERE id=#{id};
</update>

更新数据的时候,有些为空则不用更新,怎么来处理呢?

set标签的作用
  1. 用在update语句中,相当于set关键字
  2. 去掉SQL代码片段中后面多余的逗号
需求

根据id修改用户部分数据

实现

声明mapper接口方法

/**
 更新用户
 */
int updateUser(User user);

配置mapper映射文件:根据id修改用户部分数据

<!--这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新-->
<update id="updateUser" parameterType="user">
    UPDATE user SET username=#{username}, birthday=#{birthday}, sex=#{sex}, address=#{address} WHERE id=#{id};
</update>

测试

@Test
public void testUpdateUser() throws IOException {
    User u = new User();
    u.setId(26);
    u.setUsername("大王");
    userMapper.updateUser(u);
}

这样存在问题,没有值的字段也会被更新为null,最好是有数据的字段更新,没有数据的字段不更新。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r1oY9SEi-1598011240843)(/1562728386445.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TyV6s4yg-1598011240845)(/1562729023088.png)]

使用set标签进行判断,如果有值就更新,没有值就不更新。

<update id="updateUser" parameterType="user">
    UPDATE user
        <set>
            <if test="username != null and username != ''">
                username=#{username},
            </if>

            <if test="birthday != null">
                birthday=#{birthday},
            </if>

            <if test="sex != null and sex != ''">
                sex=#{sex},
            </if>

            <if test="address != null and address != ''">
                address=#{address}
            </if>
        </set>
        WHERE id=#{id};
</update>

set标签,可以按照条件拼接set后面的内容
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hyxGskCM-1598011240846)(/1562728792369.png)]

小结

set标签的作用是?

相当于SET关键字

去掉多余的,

foreach标签:遍历数组1

MyBatis得到数组中的数据动态拼接SQL

int row = userMapper.deleteUsers(new int[]{1, 3, 6, 8});

delete from user where id in (1, 3, 6, 8);
使用数组来封装要删除的所有的id

目标

foreach标签:遍历数组得到基本类型的数据

需求

遍历集合拼接SQL语句,实现批量删除用户

foreach标签介绍
foreach标签的属性作用
collection要遍历的集合,2个取值:list或array
item设置变量名,代表每个遍历的元素
separator每次遍历完后添加一个分隔符
#{变量名}取出数据
实现

声明mapper接口方法

/**
 批量删除用户
 */
int deleteUsers(int[] ids);

配置mapper映射文件

<!--根据id删除多个用户
parameterType: 如果是数组写list-->
<delete id="deleteUserByIds" parameterType="list">
    <!--DELETE FROM user WHERE id IN (1, 3, 5, 7, 8);-->

    DELETE FROM user WHERE id IN
    <!--目的,遍历参数拼接成: (1, 3, 5, 7, 8);
    foreach: 遍历数组或集合
        collection: 两个取值 遍历数组写array, 遍历集合写list
        open: 遍历前添加的内容
        close: 遍历后添加的内容
        item: 保存遍历得到的元素
        separator: 每次遍历后添加的内容
    -->
    <foreach collection="array" open="(" close=");" item="ele" separator=",">
        #{ele}
    </foreach>
</delete>

测试

@Test
public void testDeleteUser() throws IOException {
    int row = userMapper.deleteUsers(new int[]{24, 25, 26});
    System.out.println("row = " + row);
}

效果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M1K6S2CY-1598011240849)(/1562730899100.png)]

小结

foreach标签:遍历数组或集合

  • collection:要遍历的数组或集合,数组写array,集合list
  • open:遍历前拼接的内容
  • close:遍历后拼接的内容
  • item:保存遍历到的元素
  • separator:每次遍历到一个元素后拼接的内容

foreach标签:遍历集合2

目标

foreach标签:遍历集合得到自定义类型的数据

需求

使用list集合保存多个User对象,添加到数据库中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PVh3bYyt-1598011240853)(/1564461653427.png)]

提问:一条insert语句插入多条记录的MySQL语句如何编写?

-- 同时插入多条记录
insert into user (username,birthday,sex,address) values 
(#{username},#{birthday},#{sex},#{address}),
(#{username},#{birthday},#{sex},#{address}),
(#{username},#{birthday},#{sex},#{address});

/*
(#{username},#{birthday},#{sex},#{address}) 要循环的内容
*/
实现

mapper接口批量添加用户的方法

/**
  批量添加用户
  */
int addUsers(List<User> users);

配置mapper映射文件

批量新增用户,参数类型是:list

<!--批量添加多条记录-->
<insert id="addUsers" parameterType="list">
    insert into user (username,birthday,sex,address) values
    <!--
    collection 要遍历的集合使用list
    item 设置变量名,代表每个遍历的元素
    separator 每次遍历完后添加一个分隔符
    #{变量名.属性} 来引用每个属性中的值
    -->
    <foreach collection="list" item="user" separator=",">
        (#{user.username},#{user.birthday},#{user.sex},#{user.address})
    </foreach>
</insert>

测试

@Test
public void testAddUsers() throws IOException {
    List<User> users = new ArrayList<>();
    users.add(new User(null,"牛魔王", Date.valueOf("1980-01-30"),"男","火焰山"));
    users.add(new User(null,"红孩儿", Date.valueOf("2009-05-08"),"男","火云洞"));
    users.add(new User(null,"玉面狐狸", Date.valueOf("2005-11-01"),"女","狐狸洞"));

    int row = userMapper.addUsers(users);
    System.out.println("添加数据影响的行数 = " + row);
}

结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7FphySg-1598011240857)(/1562739864822.png)]

小结

foreach:可以遍历数组或集合

  • collection:遍历数组写array,遍历集合写list
  • item:保存遍历出来的元素
  • separator:每次遍历元素后拼接的内容

sql和include标签

我们发现在接口映射文件中会出现很多相同的SQL语句,每个地方都写一遍有些麻烦。我们可以把相同的SQL语句抽取出来,在需要的地方引入即可。

目标

学习sql和include标签的使用

sql和include标签的作用
  1. sql标签:定义一段SQL语句,起个名字可以重用。
  2. include标签:引入上面定义的SQL代码段。
需求实现

UserMapper.xml

<!--抽取重复的SQL并取个名字-->
<sql id="commont">
    INSERT INTO user (username,birthday,sex,address) VALUES
</sql>
<insert id="addUsers" parameterType="list">
    <!--INSERT INTO user (username,birthday,sex,address) VALUES-->
    <!--引入上面的的SQL-->
    <include refid="commont"/>

    <foreach collection="list" close=";" separator="," item="user">
        (#{user.username},#{user.birthday},#{user.sex},#{user.address})
    </foreach>
</insert>

小结

  1. sql标签的作用:可以抽取常用的SQL语句

  2. include标签的作用:可以引入抽取的SQL语句

学习总结

  1. 能够使用mybatis的基本增删改查

    3步走
    
    1.在接口中定义一个方法
    2.在接口映射文件中编写SQL语句
    3.测试
    
    <select>: 查询
    <insert>: 添加
    <update>: 修改
    <delete>: 删除
    
  2. 能够使用mybatis的条件查询

    接口中的方法:
    User findUserById(int id);
    
    接口映射文件
    <select id="findUserById" resultType="uSEr" parameterType="int">
        <!--#{id}: 会先使用?占位,会取参数id的值,赋值给?-->
        SELECT * FROM user WHERE id=#{id};
    </select>
    
    通过#{xxx}取到参数,赋值给?
    
  3. 能够使用mybatis模糊查询

    接口中的方法:
    // 通过用户名模糊查询
    List<User> findUsersByName(String username); // '%精%'
    
    接口映射文件
    <!--通过用户名模糊查询: 推荐使用-->
    <select id="findUsersByName" parameterType="string" resultType="user">
        SELECT * FROM USER WHERE username LIKE #{username};
    </select>
    
    测试类中:
    List<User> users = mapper.findUsersByName("%精%");
    
  4. 能够使用动态sql完成sql拼接

    <if test="条件">
    	SQL片段
    </if>
    
    当条件为true,就会拼接if中的SQL片段
    
    
    <where></where>
    

相当于WHERE关键字,根据需要添加或去掉WHERE关键字,还会去掉多余AND OR


相当于SET关键字, 会去掉多余的逗号


遍历数组或集合



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值