MyBatis (二) -- 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业务逻辑稳定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值