SSM框架学习之MyBatis(三)

上章回顾

  1. 我们在MyBatis(一)中介绍了有关MyBatis的相关概念:
  • 持久层框架
  • 几乎避免了所以JDBC代码和手动设置参数以及获取结果集
  • 使用了ORM思想,实现了结果集的封装
    【ORM:object Relational Mapping 对象关系映射】
  • 把数据库表和实体类以及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表
  1. 我们在MyBatis(二)中介绍了有关MyBatis的增删改查

本章内容

接下来,我们来看看,如何在Mapper.xml文件中,对数据进行增删改查。这也是最常用的增删改查,通过注解的方式只能编写一些比较简单的SQL,但在现实业务中,我们会用比较复杂的SQL对数据进行操作。
本章学习的重点是如何书写动态SQL!

步骤:

  1. 编写映射接口UserMapper
  • 声明方法(增删改查的方法声明)
  1. 编写对应的映射文件UserMapper.xml
  • 写入符合业务的SQL语句
  1. 编写单元测试类,测试功能

目录结构:
在这里插入图片描述
对应代码:

1.新增用户int addUser(User user);

  • 在UserMapper中申明一个插入数据的方法:
//    添加一个用户
    int addUser(User user);
  • 在UserMapper.xml中,编写该方法的SQL
    <insert id="addUser" parameterType="user">
        insert into user(id,username,password) values (#{id},#{username},#{password})
    </insert>
  • 测试该方法
 @Test
    public void addUserTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = new User();
        user.setId(1);
        user.setUsername("zhangsan");
        user.setPassword("zs123456");
        mapper.addUser(user);
        sqlSession.close();
    }

我们查看一下日志信息:
在这里插入图片描述
已经完成了插入操作,我们来看看数据库
在这里插入图片描述
数据库中插入一条数据,插入操作成功!

2.删除用户int removeUser(int id);

  • 在UserMapper中申明删除用户的方法
//    删除一个用户
    int removeUser(int id);
  • 在UserMapper.xml中,编写该方法的SQL
    <delete id="removeUser" parameterType="int">
        delete from user where id = #{id}
    </delete>
  • 测试该方法
@Test
    public void deleteUserTest(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        mapper.removeUser(1);
        sqlSession.close();
    }

我们来查看一下日志:
在这里插入图片描述
可以看到,这里执行了一条删除语句!

我们再来看看数据库中的数据:
在这里插入图片描述
id为1的数据被删除了!删除用户操作成功!

★3.修改用户信息int alterInfo(Map map);

  • 在UserMapper中申明一个修改方法:
//    修改用户信息
int alterInfo(Map map);
  • 在UserMapper.xml中,编写该方法的SQL
<update id="alterInfo" parameterType="map">
        update user
        <set>
            <if test="username!=null">
                username = #{username},
            </if>
            <if test="password!=null">
                password = #{password}
            </if>
        </set>
            where id = #{id}
    </update>
  • 测试该方法
    @Test
    public void updateUserInfo(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap map = new HashMap();
        map.put("id",2);
        map.put("username","zhangsan");
        map.put("password","132");
        mapper.alterInfo(map);
        sqlSession.close();
    }

我们来查看一下日志:
在这里插入图片描述
可以看到,这里id为2的用户,用户名更改为:zhangsan,密码更改为:132

再来查看一下数据库:
在这里插入图片描述
id为2的用户信息被更改了!修改用户信息操作成功!

注意:
这里使用了一对<set></set>标签,主要作用是:当需要更改的字段只有一个时,可以自动去掉“逗号”。

如,这里把username后面的逗号“去掉”了。
在这里插入图片描述
这里就把password前面的逗号“去掉”了
在这里插入图片描述
这样就会更灵活,完成更多操作!

★★★4.查询用户

4.1 查询全部用户List<User> queryAll();

  • 在UserMapper中申明查询全部用户数据的方法
//    查询全部用户信息
    List<User> queryAll();
  • 在UserMapper.xml中,编写该方法的SQL
<select id="queryAll" resultType="user">
        select * from user
</select>
  • 测试该方法
    @Test
    public void findAllUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.queryAll();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

查看日志结果:
在这里插入图片描述
这是一个比较简单的查询,直接无条件查询全部用户信息。

下面让我们来看看几个比较复杂的查询!

4.2 根据指定条件查询用户List<User> queryUser(Map map);

  • 在UserMapper中申明查询用户数据的方法
// 根据条件查询用户信息
List<User> queryUser(Map map);
  • 在UserMapper.xml中,编写该方法的SQL
<select id="queryUser" parameterType="map" resultType="user">
        select * from user
        <where>
            <choose>
                <when test="id!=null">
                    id = #{id}
                </when>
                <when test="username!=null">
                  and  username = #{username}
                </when>
                <when test="password!=null">
                  and password = #{password}
                </when>
            </choose>
        </where>
    </select>
  • 测试该方法
    @Test
    public void findUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap map = new HashMap();
        map.put("id",2);
        map.put("username","user2");
        map.put("password","123");
        List<User> userList = mapper.queryUser(map);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

输入日志信息:
在这里插入图片描述
这里我们可以看到,即便是有三个条件,但是如果第一个条件不为空,查询仅会按第一个条件来进行查询!

假如,没有第一个条件,会发生什么呢?

在这里插入图片描述
假如没有第一个id的条件,那他就会执行后面的条件,如果后面有条件,则只执行最前面的一个条件,后面的条件将不再执行。

假如,三个条件都没有,将执行什么语句呢?

在这里插入图片描述
正如日志所显示的那样,假如没有条件的话,就是查询全部的用户信息

注意:
这里除了使用了<select></select>标签对以外,还使用了三个标签对:<where></where><choose></choose><when></when>

使用<where></where>标签对,能自动把and去掉(和前面set标签对去掉逗号类似),使用<choose></choose>标签对,将从下列条件中选择性执行(有点像switch-case语句),使用<when></when>标签对,将执行满足条件的语句。

尤其注意,choose和when标签是配对使用,也就是说,要执行when标签的东西,必须要先有choose标签“包裹”在外面,类似于switch-case,有原子性,不可分割!

可能有人要问了,那我如果想查询所有条件都满足的数据,又该怎么办呢?

我们依旧用<where></where>标签对,但是里面做一点点小改动!

    <select id="queryUser" parameterType="map" resultType="user">
        select * from user
        <where>
            <if test="id!=null">
                id = #{id}
            </if>
            <if test="username!=null">
                and username = #{username}
            </if>
            <if test="password!=null">
                and password = #{password}
            </if>
        </where>
    </select>

我们把之前的choose和when全部替换成<if></if>标签对,就可以实现“查询满足全部条件的数据”了!

在这里插入图片描述
这里再针对choose和when标签做一点点补充:
引入<otherwise></otherwise>标签对,该标签对含义是:无论最后一个条件是否存在,都会将其作为条件来执行,但是如果前面有条件的时候,就会执行前面的(和之前一样)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.3 根据集合中的内容查询用户数据List<User> queryForeach(Map map);

  • 在UserMapper中申明查询用户数据的方法
    // 根据集合中的字段内容查询用户信息
    List<User> queryForeach(Map map);
  • 在UserMapper.xml中,编写该方法的SQL
<select id="queryForeach" parameterType="map" resultType="user">
        select * from user
        <where>
            <foreach collection="uid" item="id" open="(" close=")" separator="or">
                id = #{id}
            </foreach>
        </where>
    </select>
  • 测试该方法:
 @Test
    public void findUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);
        HashMap map = new HashMap();
        map.put("uid",arrayList);
        List<User> userList = mapper.queryForeach(map);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

日志显示的内容:
在这里插入图片描述
从日志显示的结果来看,该SQL的前面拼了一个“(”,中间用“or”分割,后面拼了一个“)”,该操作可以用于:当需要查询的字段值有多种情况时,将满足其中任意条件的结果返回。

关于foreach的几点补充:

当传入参数为数组或者集合时需要通过<foreach></foreach>标签进行遍历

1、首先在实体类中定义一个集合或者数组 比如 private List<Integer> ids;

2、在映射文件中<foreach collection="ids" item="ids" item="user_id" open="(" close=")" seperator="or"><foreach/>

collection:指定输入对象中集合属性

item:每次遍历生成的对象

open:开始遍历时拼接的串

close:结束遍历时两个对象需要拼接的串

★★4.4 利用sql片段进行查询List<User> queryAll2(Map map);

描述:当有大量标签重复时,为了避免代码冗余,我们可以将其作为一个SQL片段,插入到需要的标签对中。

  • 在UserMapper中申明查询用户数据的方法
//    使用SQL片段进行查询
    List<User> queryAll2(Map map);
  • 在UserMapper.xml中,编写该方法的SQL
<sql id="name-pwd">
        <if test="username!=null">
            and username = #{username}
        </if>
        <if test="password!=null">
            and password = #{password}
        </if>
    </sql>
    <select id="queryAll2" parameterType="map" resultType="user">
        select * from user
        <where>
            <include refid="name-pwd"/>
        </where>
    </select>

这里分成了两部分,容易重复的代码放在<sql></sql>标签中,并给其一个id,如果需要引用,使用<include refid="[sql标签中的id]"/>,即可调用!

  • 测试该方法
@Test
    public void findAllUser2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap map = new HashMap();
        map.put("username","zhangsan");
        List<User> userList = mapper.queryAll2(map);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

日志显示的结果:
在这里插入图片描述
该方法可以根据传入的参数进行查询操作,当传入两个参数时:
在这里插入图片描述
因为使用的是标签,中间使用“and”连接,所以当传入两个参数时,查询条件是必须同时满足这两个条件!

建议:理解标签的使用,尤其掌握和查询有关的操作!

5.分页查询List<User> queryLimit(Map map);

  • 在UserMapper中申明分页查询用户数据的方法
//  分页查询
    List<User> queryLimit(Map map);
  • 在UserMapper.xml中,编写该方法的SQL
    <select id="queryLimit" parameterType="map" resultType="User">
        select * from user limit #{startIndex},#{pageSize}
    </select>
  • 测试该方法
@Test
    public void queryUserLimit(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap map = new HashMap();
        map.put("startIndex",0);
        map.put("pageSize",3);
        List<User> userList = mapper.queryLimit(map);
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

查看日志信息:
在这里插入图片描述
该SQL是从0(也就是第一行)开始查询,查询的步长为3(一次查3行数据)

我们来看看数据库表的信息。
在这里插入图片描述
是符合要求的查询结果!说明我们的分页方法操作成功!

补充:这里我们除了使用resultType,我们还可以使用resultMap!

修改UserMapper.xml中的代码:

<resultMap id="UserMapper" type="user">
    <result property="id" column="id"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
</resultMap>
    <select id="queryLimit" parameterType="map" resultMap="UserMapper">
        select * from user limit #{startIndex},#{pageSize}
    </select>

查看日志:
在这里插入图片描述

使用resultMap和resultType的区别是:

resultType
resultType可以把查询结果封装到pojo类型中,但必须pojo类的属性名和查询到的数据库表的字段名一致。
如果sql查询到的字段与pojo的属性名不一致,则需要使用resultMap将字段名和属性名对应起来,进行手动配置封装,将结果映射到pojo中。
resultMap
resultMap可以实现将查询结果映射为复杂类型的pojo,比如在查询结果映射对象中包括pojo和list实现一对一查询和一对多查询。

6.模糊查询List<User> fuzzyQuery(String username);

  • 在UserMapper中申明模糊查询用户数据的方法
//   模糊查询
    List<User> fuzzyQuery(String username);
  • 在UserMapper.xml中,编写该方法的SQL
<select id="fuzzyQuery" parameterType="String" resultType="user">
       select * from user where username like "%"#{username}"%"
</select>
  • 测试该方法
 @Test
    public void fuzzyQueryUser(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.fuzzyQuery("user");
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

查看日志结果:
在这里插入图片描述
说明:该SQL是查询username字段的数据中,包含“user”的数据。

在mysql中,有一种函数(concat())可以通过拼接字符串达到模糊查询的目的:

修改UserMapper.xml中的代码:

<select id="fuzzyQuery" parameterType="String" resultType="user">
    select * from user where username like concat(concat('%',#{username},'%'))
</select>

日志显示的结果:
在这里插入图片描述
这两种方法都可以实现MyBatis的模糊查询。

备注:
1.其实还可以使用’%${username}%'的方式,再拼字符,但是不能防止SQL注入
2.或者直接在java代码中传入一个"%user%",但很明显不适合真正的使用环境

注意:
在运行的时候可能会出现一个异常:org.apache.ibatis.binding.BindingException
1.映射文件中,namespace中的路径和映射接口路径对应,否则扫描不到
在这里插入图片描述
2.在pom.xml中加入<build></build>,防止在build中配置resources来防止我们资源导出失败问题。

  <!--    在build中配置resources来防止我们资源导出失败问题-->
 <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

3.引入别名的时候,是为实体类的路径取别名

<typeAliases>
        <package name="com.demo.pojo"/>
</typeAliases>

在这里插入图片描述

其他

1.Lombok插件的使用说明

1.1 说明:
我们在创建实体类的时候,会创建实体类的构造方法(有参构造和无参构造),和各种get以及set方法,虽然现在可以通过idea的快捷键(alt+insert),自动导入
在这里插入图片描述
但是,大量的get和set方法会让实体类代码量变多,而且一旦有新的字段(变量)出现,就要再添加新的get,set以及有参构造方法。

那么,有什么方法可以减少重复劳动,减少代码量呢?Lombok来了!

1.2 Lombok使用

使用步骤:
1.在pom.xml文件中,导入Lombok的依赖。

<!--    Lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.16</version>
    </dependency>

2.在实体类中加入注解

package com.demo.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
 * @author: seh
 * @date: 2020/10/25 9:04
 * @version: 1.0
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private int id;
    private String username;
    private String password;
}

加入三个注解,就能把原先大段的get和set方法以及有参、无参构造方法省略。
@Data@NoArgsConstructor@AllArgsConstructor

我们来测试一下!
在这里插入图片描述
之前写的模糊查询的操作没有问题!操作成功!

那么为什么加了Lombok的依赖后,使用三个注解就可以实现?

点击idea中的【structrure】
在这里插入图片描述
原来,注解内部已经封装好了,@Data包括了这些方法
在这里插入图片描述
@NoArgsConstructor@AllArgsConstructor就是添加有参构造和无参构造了!

可能大家觉得Lombok插件“太棒了!”

但实际上,如果使用了这个插件,对于不清楚内部构造的人来说,根本看不懂实体类。

更重要的是:
如果你想确认某个set或get方法是否在程序中被调用,你无法找到哪里使用的,但是我更认为这样的操作是违背了bean使用的初衷,bean尤其数据库和java类的映射bean,java对bean的定义和使用就是无参数的构造方法和set和get方法,而不应该在bean中处理任何和业务有任何关系的逻辑。

所以,对于Lombok的评价,仁者见仁,智者见智吧!

2.log4j日志说明

这是Mybatis官网对于日志的介绍:
https://mybatis.org/mybatis-3/zh/logging.html
2.1 什么是log4j?

  • Log4j是Apache的一个开放源代码项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等

  • 我们也可以控制每一条日志的输出格式

  • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程

  • 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码

我们前面介绍过标准的日志输出(STDOUT_LOGGING),接下来我们来看看如何在项目中配置log4j日志。

2.2配置log4j的步骤:

1.在pom.xml中配置log4j的依赖

    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>

2.在mybatis-config.xml加入setting设置

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

在这里插入图片描述
setting的位置千万不能错!

3.在resources目录下创建一个log4j的配置文件

log4j.properties文件的配置内容如下:

log4j.rootLogger=DEBUG,console,file

#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/tomcat.txt
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n

#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG

这样配置完成以后就可以运行了!
在这里插入图片描述
运行完以后,在项目的目录树中出现一个新的log目录
在这里插入图片描述
在这里插入图片描述
日志目录和日志文件可以在log4j.properties文件中设置。

log4j.appender.file.File=./log/tomcat.txt

2.3 日志级别

 logger.info("info:进入了log4j");
 logger.debug("debug:进入了log4j");
 logger.error("error:进入了log4j");

2.3.1 测试日志级别

static Logger logger = Logger.getLogger(TestCheck.class);
    @Test
    public void testLog4j(){
        logger.info("info:进入了log4j");
        logger.debug("debug:进入了log4j");
        logger.error("error:进入了log4j");
    }

2.3.2 控制台信息
在这里插入图片描述
2.3.3 日志文件信息
在这里插入图片描述
由此可见,运行的日志会一直往后添加(默认append是true

我们设置成false 就不追加了 直接覆盖前面的内容,或者手动清除日志。

只需要在log4j.properties文件中加入如下的语句:

log4j.appender.file.Append = false

在这里插入图片描述
运行结果:
在这里插入图片描述
关于MyBatis的内容到这里就结束了!分页插件PageHelper、mybatis-plus等内容后期有需要再更新。(可能之后还会更新一个Redis的内容)

之后会更新Spring、SpringMVC以及SSM整合的项目,请持续关注!(ps:终于写完了QAQ)

更多内容,请关注公众号:
在这里插入图片描述
源码的链接我会放评论区。

创作不易,如果觉得还可以的话,给个三连,拜托了!

源码地址:

github地址:https://github.com/Holmes-SiEnhao/SSM-.git

gitee地址:https://gitee.com/sienhao/ssm-framework-learning.git

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值