MyBatis (二) -- MyBatis中的 查询 & 增删改 & 动态SQL语句 & 注解开发

本文深入探讨MyBatis框架的高级应用,包括列名与属性名映射、动态SQL、多表连接查询、注解开发等核心功能,助力开发者掌握高效数据库交互技巧。

1 解决列名和属性名不匹配的问题

默认情况下, MyBatis会进行自动映射(Auto-Mapping), 指的是MyBatis能够自动将同名的列和属性进行映射

1.1 在 SQL 语句中给列名起别名

 SQL语句复用度低  无法处理多变关联查询问题

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="a.b.c">
    <select id="selAll" resultType="com.wuyw.pojo.User">
        select id, username, password, real_name realName, 
            age, birthday, reg_time regTime from tb_user
    </select>
</mapper>

1.2 让MyBatis自动解决

在MyBatis核心配置文件中开启自动驼峰命名, 当MyBatis发现有带下划线的列名时, 会自动转换为对应的驼峰命名.

例如: reg_time --> regTime.

注意: settings标签要配置在environments标签的前面.

<!--配置系统设置-->
<settings>
    <!--开启自动驼峰命名-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

1.3 定义映射关系

映射关系可以在多处使用 可以处理数据库表格之间一对一 一对多  多对多的关系

<?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.wuyw.mapper.UserMapper">
    <!--手动定义映射关系-->
    <resultMap id="userMap" type="user">
        <!--id标签对应主键列-->
        <id column="id" property="id" />
        <!--result标签对应非主键列-->
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="real_name" property="realName"/>
        <result column="age" property="age"/>
        <result column="birthday" property="birthday"/>
        <result column="reg_time" property="regTime"/>
    </resultMap>

    <select id="selAll" resultMap="userMap">
        select * from tb_user
    </select>
</mapper>

2 查询常用的三种方法

三个常用的查询方法: selectList, selectOne和selectMap, 都允许在调用的时候传递参数, 参数的类型是Object, 个数只能传递一个. 如果需要传递多个参数, 需要处理成单个对象. 同时, SQL语句也需要相应的修改, 要使用占位符替代接收的参数.

2.1 selectList

用于查询多条数据, MyBatis会自动将查到的数据封装为List集合.

最常用, 查不到时返回空集合, 而不是null.

2.2 selectOne

用于查询单条数据, 结果是指定的某个对象或者某个值. 很常用. 底层调用了selectList, 如果查到一个值, 直接返回结果,

如果查到多个值, 抛出异常, 如果查不到, 返回null.

2.3 selectMap

用于将查询结果转换为Map集合, 使用时需要指定将结果中的哪个属性作为map的key, Map的value就是当前对象.

一般不用. 底层调用selectList.

3 占位符

占位符有两种, 分别是#{}, ${}.

#{}底层使用的是PreparedStatement, ${}底层使用的是Statement.

3.1 #{} 占位符

#{}作为SQL语句占位符时, MyBatis允许接收三种类型的参数:

1)简单类型的参数

 

简单类型指的是基本数据类型, 包装类型, String, java.sql.*...,

此时, #{}会忽略占位符的名称和个数, 将参数进行绑定. parameterType可以指定参数的类型, 如果省略, 表示参数类型为Object.

一旦指定, 传参时必须类型一致, 否则抛出类型转换异常.

<select id="selById" resultType="com.wuyw.pojo.User" parameterType="java.lang.String">

    select * from tb_user where id=#{id} or username=#{suibian}

  </select>
@Test

  public void testSelById() {

    SqlSession session = MyBatisUtil.getSession();

    session.selectOne("selById", 3);

    session.close();

}

 2)Map类型的参数

多个参数传递时使用, 将多个参数封装为Map集合, #{}需要使用Map中对应的key去取值, 如果key不存在, 不会报错, 拿到的是null

<select id="sel4Login" resultType="com.wuyw.pojo.User">

    select * from tb_user where username=#{uname} and password=#{upwd}

  </select>
@Test

  public void testSel4LoginByMap() {

    SqlSession session = MyBatisUtil.getSession();

    Map<String, Object> params = new HashMap<>();

    params.put("uname", "lisi");

    params.put("upwd", "123");

    session.selectOne("sel4Login", params);

    session.close();

}

3)POJO类型的参数

多个参数封装为对应的POJO类, 此时, #{}需要调用该POJO类中对应的 getter 方法取值, 如果没有对应的getter, 则抛出异常.

<select id="sel4Login" resultType="com.wuyw.pojo.User">

    select * from tb_user where username=#{username} and password=#{password}

  </select>
@Test

  public void testSel4LoginByPojo() {

    SqlSession session = MyBatisUtil.getSession();

    User user = new User();

    user.setUsername("wangwu");

    user.setPassword("123");

    session.selectOne("sel4Login", user);

    session.close();

}

当SQL语句结构不确定时, 不能使用#{}占位符,

原因是#{}底层使用的是PreparedStatement, 会对SQL语句进行预编译, 导致SQL语句的结构无法被补全.

使用${}来进行操作.

3.2 ${}占位符

${}底层使用的是Statement实现, 做的是字符串的直接拼接.

SQL语句结构固定的情况下, 应该使用#{}占位符, 当SQL语句结构不确定是, 只能使用${}实现.

<select id="sel" resultType="com.wuyw.pojo.User">
    select * from ${tbName} where ${cname}='${cvalue}' ${order}
</select>
@Test
public void test() {
    SqlSession session = MyBatisUtil.getSession();

    Map<String, Object> params = new HashMap<>();
    params.put("tbName", "tb_user");
    params.put("cname", "reg_time");
    params.put("cvalue", "2019-12-24 15:18:35");
    params.put("order", "order by id desc");

    session.selectList("sel", params);
    session.close();
}

4 实现增删改操作

增删改操作涉及到事务管理, MyBatis中, 默认将JDBC的自动管理事务机制关闭了. 要求所有增删改操作都必须进行手动的事务管理. 通过SqlSession来进行事务管理.

4.1  新增数据

  • 标签: 使用<insert>标签
  • 方法: 使用insert()
  • useGeneratedKeys=”true”表示要获取自动增长的主键, keyProperty=”id”表示将获取到的主键值赋值给id属性, 会自动调用setId方法
<!--新增操作-->
<insert id="ins" useGeneratedKeys="true" keyProperty="id">
    insert into tb_user values (
        default, #{username}, #{password}, #{realName}, #{age}, #{birthday}, now()
    )
</insert>
@Test
public void testInsert() {
    SqlSession session = MyBatisUtil.getSession();
    User user = new User(20, "zhaomin3", "123", "赵敏", new Date());
    // 新增前打印user对象
    System.out.println(user);
    int rows = session.insert("ins", user);
    if(rows == 1) {
        // 提交事务
        session.commit();
        System.out.println("ok");
    } else {
        System.out.println("error");
    }
    // 新增后打印user对象
    System.out.println(user);

    session.close();
}

4.2 更新数据

  • 标签: 使用<update>标签
  • 方法: 使用update()
<!--更新操作-->
<update id="upd">
    update tb_user set username=#{uname}, real_name=#{rname} where id=#{id}
</update>
@Test
public void testUpdate() {
    SqlSession session = MyBatisUtil.getSession();

    Map<String, Object> params = new HashMap<>();
    params.put("uname", "xiexun");
    params.put("rname", "谢逊");
    params.put("id", 9);

    try {
        session.update("upd", params);
        session.commit();
    } catch (Exception e) {
        session.rollback();
        e.printStackTrace();
    }

    session.close();
}

4.3 删除数据

  • 标签: 使用<delete>标签
  • 方法: 使用delete()
<!--删除操作-->
<delete id="del">
    delete from tb_user where id=#{id}
</delete>
@Test
public void testDelete() {
    SqlSession session = MyBatisUtil.getSession();
    session.delete("del", 8);
    session.commit();
    session.close();
}

5 类型别名

主要针对映射文件中定位类型时使用, 简化配置. 分为两种情况:

5.1 MyBatis内建别名

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

别名

映射的类型

别名

映射的类型

别名

映射的类型

_byte

byte

string

String

decimal

BigDecimal

_long

long

byte

Byte

bigdecimal

BigDecimal

_short

short

long

Long

object

Object

_int

int

short

Short

map

Map

_integer

int

int

Integer

hashmap

HashMap

_double

double

integer

Integer

list

List

_float

float

double

Double

arraylist

ArrayList

_boolean

boolean

float

Float

collection

Collection

 

 

boolean

Boolean

iterator

Iterator

 

 

date

Date

 

 

 

 

 

 

 

 

 

 

 

 

 

5.2 自定义别名

<typeAliases>
    <!--<typeAlias type="com.wuyw.pojo.User" />-->
    <package name="com.wuyw.pojo"/>
</typeAliases>

6 动态SQL语句

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

6.1 <if>

用于条件判断, test属性表示判断结果, 要求是一个boolean值.

6.2 <where>

用于维护where子句, 通常配合<if>一起使用. 如下功能:

  • 当没有条件时, 不会创建WHERE关键字;
  • 当有条件时, 会自动生成WHERE关键字;
  • 会自动去掉第一个条件的and/or关键字.
<mapper namespace="com.wuyw.mapper.UserMapper">

    <select id="selByCondition" resultType="user">

        select * from tb_user

        <where>

            <!-- test属性用于定义条件表达式 -->

            <if test="username != null and username != ''">

                and username = #{username}

            </if>

            <if test="age != null">

                and age = #{age}

            </if>

        </where>

    </select>

  </mapper>

6.3 <choose><when><otherwise>

功能类似于java中的switch...case...default. 多分支判断, 只能成立一个条件.

6.4 <bind>

对数据进行加工, 通常用于处理模糊查询的数据.

6.5 <sql><include>

提取SQL片段和引用SQL片段

<mapper namespace="com.wuyw.mapper.UserMapper">

    <!--定义SQL片段-->
    <sql id="sel_sql">    
        select id, username, password, real_name, 
        age, birthday, reg_time from tb_user
    </sql>

    <select id="selByCondition" resultType="user">
        <!-- 引用SQL片段 -->
        <include refid="sel_sql" />
        <where>
            <!-- test属性用于定义条件表达式 -->

            <if test="username != null and username != ''">
                <!-- 对参数进行加工, 通常用于处理模糊查询的数据 -->
                <bind name="username" value="'%' + username + '%'"/>
               and username like #{username}
            </if>

            <if test="age != null">
                and age &gt; #{age}
            </if>

            <choose>
                <when test="birthday != null and birthday != ''">
                    and birthday = #{birthday}
                </when>

                <otherwise>
                    and birthday is null
                </otherwise>
            </choose>
        </where>
    </select>
  </mapper>

6.6 <set>

用于维护更新语句中的set子句, 类似于<where>的功能.

  • 当条件不成立时, 不会创建SET关键字;
  • 当条件成立时, 会自动生成SET关键字;
  • 会自动去掉最后一个条件的逗号(,).
<update id="updUser">
    update tb_user
        <set>
            id = #{id},
            <if test="username != null and username != ''">
                username=#{username},
            </if>
            <if test="password != null and password != ''">
                password=#{password},
            </if>
            <if test="age != null">
                age=#{age},
            </if>
        </set>
    where id=#{id}
</update>

6.7 <foreach>

对数组, List, Set, Map集合进行遍历时使用, 通常用于in操作中. 一般用于实现批量新增或批量删除操作. 属性介绍:

  • collection: 要遍历的集合
  • item: 迭代项
  • open: 以什么字符开头
  • close: 以什么字符结束
  • separator: 多个迭代项之间的分隔符
<delete id="delUser">
    delete from tb_user where id in
    <foreach collection="ids" open="(" close=")" separator="," item="id">
        #{id}
    </foreach>
 </delete>
@Test
  public void testForeach() {
    SqlSession session = MyBatisUtil.getSession();
    UserMapper mapper = session.getMapper(UserMapper.class);
    // int[] ids = {1, 2, 3, 4, 5, 6};
    // List<Integer> ids = new ArrayList<>();
    // Set<Integer> ids = new HashSet<>();
    // Collections.addAll(ids, 1, 3, 5, 7);
    Map<Integer, Integer> ids = new HashMap<>();
    ids.put(1, 11);
    ids.put(2, 22);
    ids.put(3, 33);
    mapper.delUser(ids);
    session.close();
}

6.8 <trim>

用于对数据进行处理, 可以在指定的字符串前后进行操作.

<insert id="insUser">
    insert into tb_user values
    <foreach collection="users" separator="," item="u">
        <trim prefix="(" prefixOverrides="," suffix=")" suffixOverrides=",">
            ,default, #{u.username}, #{u.password}, #{u.realName},
             #{u.age}, #{u.birthday}, now(),
        </trim>
    </foreach>
  </insert>

6.9 <resultMap>标签的使用

默认情况下, MyBatis会进行自动映射(Auto-Mapping), 数据库表格的列名和对象的属性名如果同名, MyBatis会进行自动赋值. 当列名和属性名不同., MyBatis允许程序员自己定义列名和属性名的映射关系.

<resultMap>需要配置resultMap属性使用.

如果使用了resultType属性, 表示自动映射, 如果使用了resultMap属性, 表示手动映射.

  • 同名可以省略不写;
  • id标签要写在result标签的前面;
<?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.wuyw.mapper.UserMapper">
    <!--手动定义映射关系-->
    <resultMap id="userMap" type="user">
        <!--id标签对应主键列-->
        <id column="id" property="id" />
        <!--result标签对应非主键列-->
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="real_name" property="realName"/>
        <result column="age" property="age"/>
        <result column="birthday" property="birthday"/>
        <result column="reg_time" property="regTime"/>
    </resultMap>

    <select id="selAll" resultMap="userMap">
        select * from tb_user
    </select>
</mapper>

8  多表连接查询

在MyBatis中, 多表连接查询主要分为两种, 一种是多对一, 一种是一对多.

示例数据 

create table tb_class (
    id integer primary key auto_increment,
    name varchar(10) not null,
    room varchar(10)
);

# 创建学生表
create table tb_student (
    id integer primary key auto_increment,
    name varchar(20) not null,
    gender char(1),
    birthday date,
    cid integer
);

# 添加数据
insert into tb_class values
(default, 'Java', '1601'),
(default, '计算机网络', '1602'),
(default, '数据结构', '1601');

insert into tb_student values
(default, '张三', '男', '1998-12-12', 1),
(default, '李四', '女', '1999-12-12', 2),
(default, '王五', '男', '1998-12-12', 2),
(default, '赵六', '男', '1998-12-12', 3),
(default, '孙七', '女', '1998-12-12', 3),
(default, '吴八', '女', '1998-12-12', 3);

8.1 多对一查询

   多个学生对应一个班级, 查询所有学生信息, 同时查询每个学生所在班级的信息

8.1.1 实体类

在学生实体类中, 应该提供一个班级类型的对象, 用来表示班级信息.

public class Student implements Serializable {
    private Integer id;
    private String name, gender;
    private Date birthday;
    private Integer cid;
    private Clazz clazz;
}

8.1.2 关联方式实现查询

指的是通过SQL语句关联查询的方式实现. 表1 joint 表2 on 关联条件.

在MyBatis中, 关联单个对象需要使用<association>标签进行.

<?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.wuyw.mapper.StudentMapper">
    <resultMap id="smap" type="student" autoMapping="true">
        <!--关联单个对象, 理解为小的resultMap即可-->
        <association property="clazz" javaType="clazz" autoMapping="true">
            <id column="cid" property="id" />
            <result column="cname" property="name" />
        </association>
    </resultMap>

    <select id="selAll" resultMap="smap">
        select
             s.*, c.name cname, c.room
        from
             tb_student s
                 join
             tb_class c
                 on s.cid = c.id
    </select>
</mapper>

8.1.3 N+1方式实现

指的是完成本次查询一共需要执行N+1条SQL语句. 每条SQL都是单表查询.

<mapper namespace="com.wuyw.mapper.StudentMapper">
    <resultMap id="smap2" type="student">
        <result column="cid" property="cid" />
        <association
                property="clazz"
                javaType="clazz"
                select="com.wuyw.mapper.ClazzMapper.selById"
                column="cid"/>
    </resultMap>

    <select id="selAll2" resultMap="smap2">
        select * from tb_student
    </select>
</mapper>

8.2  一对多查询

指的是一个班级有多个学生, 查询班级时把当前班级的所有学生也查到.

8.2.1 实体类

public class Clazz implements Serializable {
    private Integer id;
    private String name, room;
    private List<Student> list = new ArrayList<>();
}

8.2.2 关联方式实现

<mapper namespace="com.wuyw.mapper.ClazzMapper">
    <resultMap id="cmap" type="clazz" autoMapping="true">
        <!--关联集合对象-->
        <collection property="students" javaType="list" ofType="student" autoMapping="true">
            <id column="sid" property="id" />
            <result column="sname" property="name" />
            <result column="id" property="cid" />
        </collection>
    </resultMap>

    <select id="selAll" resultMap="cmap">
        select
            c.*, s.id sid, s.name sname, s.gender, s.birthday
        from
            tb_class c
                join
            tb_student s
                on
            c.id=s.cid
    </select>
</mapper>

8.2.3 N+1方式实现

<mapper namespace="com.wuyw.mapper.ClazzMapper">

    <resultMap id="cmap2" type="clazz">
        <id column="id" property="id" />
        <collection
                property="students"
                javaType="list"
                ofType="student"
                select="com.wuyw.mapper.StudentMapper.selByCid"
                column="id" />
    </resultMap>

    <select id="selAll2" resultMap="cmap2">
        select * from tb_class
    </select>
</mapper>

 

9 Mybatis的注解开发

MyBatis中常用的注解,使用MyBatis中的注解, 可以简化映射文件的配置. 注意, 动态SQL不能简化.

  • @Select, 用于简化查询配置
  • @Insert, 用于简化新增配置
  • @Update, 用于简化更新配置
  • @Delete, 用于简化删除配置
public interface DemoMapper {
    @Select("select * from tb_user")
    List<User> selAll();

    @Delete("delete from tb_user where id=#{id}")
    void delById(int id);
}

注解模式使用注意事项

1使用注解的接口可以同时存在mapper映射文件

2接口上使用了注解 在映射文件中就不能在定义对应的SQL语句标签

3使用了注解的接口 接口中并不是所有的方法都必须使用注解 个别方法使用映射文件OK

注解模式开发的好处

简化mapper映射文件

注解模式开发的缺点

将SQL语句转移到了代码中  修改代码需要重新编译项目 耦合度高

没有办法独立处理一对一 一对多  多对多的关联关系

什么情况下使用注解开发?

1业务逻辑简单

2业务逻辑稳定

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值