课堂笔记

### 1. 关于MyBatis的基本使用
 
1. 添加一系列依赖:`mysql-connector-java`、`commons-dbcp`、`junit`、`mybatis`、`mybatis-spring`、`spring-context/spring-webmvc`、`spring-jdbc`;
 
2. 关于配置:配置数据库连接、在Spring中读取数据库连接的配置、配置数据源、配置接口文件的位置、配置SQL语句的XML文件的位置及所使用的数据源;
 
3. 关于抽象方法:必须写在接口中;如果需要执行的操作是增、删、改类型的,使用`Integer`作为返回值类型,如果需要执行的操作是查询类型的,则将返回值声明为所期望的类型(能够封装查询结果的类型);方法的名称可以自定义,不允许重载;可以根据所执行的SQL语句中不确定的值(SQL语句中有哪些问号)来设计方法的参数列表,当方法的参数超过1个时,必须为每一个参数添加`@Param`注解,并在注解中指定名称;
 
4. 关于配置XML映射:XML的根节点必须有文档声明;根节点`<mapper>`必须配置对应的接口;根据需要执行的SQL语句的类型选择子级节点;如果执行的是增加数据的操作,可以配置`useGeneratedKeys="true"`和`keyProperty="属性名"`来获取自增的字段的值;如果执行的是查询操作,则必须配置`resultType`或`resultMap`中的1个属性(二选一),在配置`resultType`时,取值为返回值类型的全名,如果返回值是`List`集合类型的,只需要配置为集合中的元素的类型,例如返回`List<User>`时,配置`resultType`的值就是`User`类的全名;在编写SQL语句时,所有的变量值都使用`#{}`格式的占位符,占位符中的名称应该是参数名,或对象型的参数中的属性名;
 
5. 每写完一个功能,或者改动了一个功能,都应该及时的执行单元测试!
 
### 2. 动态SQL--foreach【重要】
 
动态SQL:在配置SQL语句时,可以添加例如`<if>`、`<foreach>`相关的配置,最终可以根据参数值不同,生成不同的SQL语句!
 
在配置MyBatis中映射的SQL语句时,可以添加`<foreach>`实现对参数的遍历!
 
例如:需要实现“根据若干个id批量删除用户数据(一次性删除若干条)”。
 
需要执行的SQL语句大致是`delete from t_user where id in (?,?,?)`,其中,问号的数量是开发者无法确定的,应该是由软件的使用者来决定的!
 
在设计抽象方法时,应该使用某1个参数来表示若干个即将被删除的数据的id,例如使用`List<Integer>`就可以表示多个`id`,也可以使用`Integer[]`或`Integer...`,所以,抽象方法可以设计为:
 
    Integer deleteByIds(List<Integer> ids);
 
    Integer deleteByIds(Integer[] ids);
 
    Integer deleteByIds(Integer... ids);
 
然后,需要配置以上某个抽象方法的映射:
 
    <delete id="deleteByIds">
        DELETE FROM t_user WHERE id IN (
            <foreach collection="array" item="id" separator=",">
                #{id}
            </foreach>
        )
    </delete>
 
关于`<foreach>`节点的配置:
 
- `collection`属性:表示被遍历的参数!当抽象方法的参数只有1个时,当参数的类型是`List`集合时,取值为`list`,当参数的类型是数组或可变参数时,取值为`array`;当抽象方法的参数有多个时,肯定添加了`@Param`注解,此处的取值就是注解中配置的名称;
 
- `item`属性:表示遍历过程中每个元素的名称,也是子级中`#{}`中的名称,是自定义的名称;
 
- `separator`属性:表示遍历的各个值用什么符号分隔;
 
- `open`属性与`close`属性:表示遍历生成的SQL语句片断的最左侧字符串和最右侧字符串。
 
### 3. 动态SQL--if【一般】
 
关于`<if>`的使用格式大致是:
 
    <select id="find"  
        resultType="cn.tedu.mybatis.User">
        SELECT * FROM t_user
        <if test="where != null">
        WHERE  
            ${where}
        </if>
        <if test="orderBy != null">
        ORDER BY  
            ${orderBy}
        </if>
        <if test="offset != null and count != null">
        LIMIT  
            #{offset}, #{count}
        </if>
    </select>
 
注意:强烈不推荐使用这种做法,使用1个查询的配置完成若干种查询需求!
 
### 4. 关于#{}和${}格式的占位符【理解】
 
最简化原则:先全部使用`#{}`格式的占位符,如果不可用,则改为`${}`格式的占位符!
 
使用`#{}`占位符时,MyBatis是预编译处理的,即各个占位符都会先使用`?`进行占位,并进行编译,编译通过后,再将值代进SQL语句中执行;简单的说,使用`#{}`格式的占位符只能表示某个“值”,并且,由于是预编译的,所以,不必关心值的类型,例如值是字符串类型的,也不必考虑使用单引号`'`框住值的问题;
 
使用`${}`占位符时,MyBatis并不会预编译,而是先将占位符中的值代进SQL语句,进行单纯的字符串拼接,然后再编译,最后执行,所以,必须保证拼接后的SQL语句是符合SQL语法的,否则会导致编译错误,最直接的表现就是例如字符串类型的值需要使用单引号`'`框住,同时,由于并没有使用编译的做法,还存在SQL注入的风险!简单的说,使用这种格式的占位符时,既需要考虑值的数据类型的问题,还需要考虑SQL注入的风险,应该是“能不这么用,就不这么用”!
 
### 5. 课堂练习
 
准备工作:
 
创建“部门信息表”(`t_department`),要求在该表中至少中存在`id`和部门名称(`name`)这2个字段:
 
    CREATE TABLE t_department (
        id INT AUTO_INCREMENT,
        name VARCHAR(10) UNIQUE NOT NULL,
        PRIMARY KEY (id)
    ) DEFAULT CHARSET=UTF8MB4;
 
然后,向该数据表中插入不少于3条记录。
 
    INSERT INTO t_department (name) VALUES ('软件研发部'), ('人力资源部'), ('财力部');
 
课堂练习:使用MyBatis框架实现:
 
1. 向部门表中插入部门信息;
 
2. 批量删除部门信息;
 
3. 查询id=?的部门的信息;
 
4. 查询所有部门的信息。
 
### 6. 使用自定义的别名解决名称不匹配的问题
 
准备工作:在“用户表”中增加新的字段“部门(department_id)”,用于表示某个用户归属于哪个部门!
 
    ALTER TABLE t_user ADD COLUMN department_id INT;
 
然后,为用户分配部门:
 
    UPDATE t_user SET department_id=2 WHERE id IN (4);
    UPDATE t_user SET department_id=4 WHERE id IN (6,8,9);
    UPDATE t_user SET department_id=6 WHERE id IN (5,7);
 
接下来,还应该在`User`类中添加新的属性`private Integer departmentId;`,并为该属性添加SET/GET方法,重新生成`toString()`方法!
 
再次查询时,会发现:查询的输出结果中,`departmentId`的值都是`null`!其原因是:查询到了相关的数据,却因为名称不匹配(数据表中的是`department_id`,而`User`类中的是`departmentId`),所以,MyBatis框架就无法正确的将结果封装到`User`类的对象中!
 
其实,MyBatis框架在执行查询并处理查询结果时,默认情况下,要求**查询结果的列名(Column)与封装结果的类的属性名(Property)必须一致**!
 
所以,在查询时,可以自定义别名,使得列名与属性名保持一致!例如:
 
    <select id="findAll"
        resultType="cn.tedu.mybatis.User">
        SELECT  
            id, username,
            password, age,
            phone, email,
            department_id AS departmentId  
        FROM  
            t_user  
        ORDER BY  
            id
    </select>
    
### 7. 使用resultMap解决名称不匹配的问题
 
在配置SQL语句的XML文件中,可以配置`<resultMap>`节点,该节点的配置就是用于指导MyBatis如何将查询结果封装到返回结果中!
 
    <!-- id属性:自定义名称 -->
    <!-- type属性:封装查询结果的数据类型的全名 -->
    <resultMap type="cn.tedu.mybatis.User"  
        id="UserMap">
        <!-- 将哪一列的结果封装到哪个属性中去 -->
        <result column="id" property="id" />
        <result column="username" property="username" />
        <result column="password" property="password" />
        <result column="age" property="age" />
        <result column="phone" property="phone" />
        <result column="email" property="email" />
        <!-- 在配置单表查询结果时,以上名称匹配的,其实可以不必配置 -->
        <result column="department_id" property="departmentId" />
    </resultMap>
 
在配置`<select>`节点时,就应该使用`resultMap`属性指定以上`<resultMap>`节点的`id`:
    
    <select id="findById" resultMap="UserMap">
        SELECT * FROM t_user WHERE id=#{id}
    </select>
 
### 8. 关联表查询
 
需求:根据id查询某个用户信息,在查询结果中,要求体现该用户所在的部门的名称!
 
需要执行的SQL语句大致是:
 
    select  
        t_user.id, username,
        password, age,
        phone, email,
        department_id AS departmentId,  
        name AS departmentName
    from  
        t_user  
    left join  
        t_department  
    on  
        t_user.department_id=t_department.id  
    where  
        t_user.id=6;
 
在设计抽象方法时,没有某个实体类能够封装查询结果!则需要创建新的VO(Value Object)类来封装查询结果:
 
    public class UserVO {
 
        private Integer id;
        private String username;
        private String password;
        private Integer age;
        private String phone;
        private String email;
        private Integer departmentId;
        private String departmentName;
 
        // SET/GET/toString
    }
 
 
> 实体类是与数据表相对应的,而VO类是与查询结果相对应的!从编写代码的角度来看,VO类与实体类是高度相似的,只是定位不同!
 
创建好VO类后,就可以设计抽象方法:
 
    UserVO findVOById(Integer id);
 
然后,配置以上抽象方法的映射:
 
    <select id="findVOById" resultType="cn.tedu.mybatis.UserVO">
        select  
            t_user.id, username,
            password, age,
            phone, email,
            department_id AS departmentId,  
            name AS departmentName
        from  
            t_user  
        left join  
            t_department  
        on  
            t_user.department_id=t_department.id  
        where  
            t_user.id=#{id}
    </select>
    

### 1.  
 
需求:根据id查询部门信息,并且,同时查出该部门的员工信息。
 
分析:需要执行的SQL语句大致是:
 
    SELECT
        *
    FROM
        t_department
    LEFT JOIN
        t_user
    ON
        t_department.id=t_user.department_id
    WHERE
        t_department.id=?
 
在开发功能时,必须先创建VO类,用于封装此次的查询结果:
 
    public class DepartmentVO {
        private Integer id;
        private String name;
        private List<User> users;
    }
 
然后在`DepartmentMapper`接口中添加抽象方法:
 
    DepartmentVO findVOById(Integer id);
 
接下来,在**DepartmentMapper.xml**中配置以上抽象方法的映射:
 
    <select id="findVOById"  
        resultType="cn.tedu.mybatis.DepartmentVO">
        SELECT
            t_department.id AS did, name,
            t_user.*
        FROM
            t_department
        LEFT JOIN
            t_user
        ON
            t_department.id=t_user.department_id
        WHERE
            t_department.id=#{id}
    </select>
 
目前,MyBatis框架并不知道如何将找到的若干条结果正确的封装到返回的`DepartmentVO`对象中!所以,需要配置`<resultMap>`!配置代码如下:
 
    <resultMap id="DepartmentVOMap"
        type="cn.tedu.mybatis.DepartmentVO">
        <id column="did" property="id" />
        <result column="name" property="name" />
        <!-- collection节点:用于配置1对多的查询结果 -->
        <!-- property属性:类中的哪个属性用于封装多个结果中的数据 -->
        <!-- ofType属性:集合属性中的元素的类型,即:这个List集合中放的是什么类型的数据 -->
        <collection property="users"
            ofType="cn.tedu.mybatis.User">
            <id column="id" property="id" />
            <result column="username" property="username" />
            <result column="password" property="password" />
            <result column="age" property="age" />
            <result column="phone" property="phone" />
            <result column="email" property="email" />
            <result column="department_id" property="departmentId" />
        </collection>
    </resultMap>
 
**注意:此次涉及多表查询,即使名称对应(查询结果的列名与期望封装到的对象的属性名是相同的),也必须显式的配置!**
 
则对应的查询节点就应该使用`resultMap`属性来配置,并且,在查询时,为了避免出现2个`id`列,会导致无法正确的封装查询结果,所以,需要为至少其中1列定义别名:
 
    <select id="findVOById" resultMap="DepartmentVOMap">
        SELECT
            t_department.id AS did, name,
            t_user.*
        FROM
            t_department
        LEFT JOIN
            t_user
        ON
            t_department.id=t_user.department_id
        WHERE
            t_department.id=#{id}
    </select>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

饭九钦vlog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值