mybatis框架--2

1.通过dao和映射文件的关联来完成操作---企业开发模式

思考: 我们之前使用SqlSession封装的一些方法可以完成crud操作,但是SqlSession封装的方法,传递的参数statement, 传递占位符的参数只能传递一个。而且他的方法名称都是固定。而真实在开发环境下我们不使用SqlSession封装的方法,而是习惯自己定义方法,自己调用自己的方法。

接口结合映射文件一起用。

1.1. 如何实现

(1)创建一个dao接口并定义自己需要的方法。

package com.wzh.dao;

import com.wzh.entity.User;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @ProjectName: mybatis0601_1
 * @Package: com.wzh.dao
 * @ClassName: UserDao
 * @Author: 
 * @Description: 用户的操作接口
 * @Date: 2022/6/1 18:40
 * @Version: 1.0
 */
public interface UserDao {
    /**
    * 方法描述
    * 查询所有
    * @return:
    */
    public List<User> findAll();
    /**
    * 方法描述
    * 根据id查询单条记录
    * @return:
    */
    public User findOne(int id);
    /**
    * 方法描述
    * 添加
    * @return:
    */
    public int insert(User user);
    /**
    * 方法描述
    * 根据id删除
    * @return:
    */
    public int delete(int id);
    /**
    * 方法描述
    * 修改
    * @return:
    */
    public int update(User user);
    /**
    * 方法描述
    * 根据账号和密码查询用户
    * @return:
     * 使用@Param为参数起名 那么在映射文件中就可以使用该名称  不然要用param1 param2 ....
    */
    public User findByNameAndPwd(@Param("username") String username,@Param("password") String password);
}

(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">
<!--namespace:命名空间
       可以随便起名,但是后期我们要求命名空间的值必须和所对应的dao相同
-->
<mapper namespace="com.wzh.dao.UserDao">
    <!--useGeneratedKeys:设置使用生成的主键
          keyProperty: 赋值给哪个属性   -->
    <insert id="insert" parameterType="com.wzh.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values(null,#{username},#{password},#{relname})
    </insert>
    <delete id="delete">
        delete from t_user where id=#{id}
    </delete>
    <update id="update">
        update t_user set username=#{username},password=#{password},relname=#{relname} where id=#{id}
    </update>
    <!--查询 根据id查询用户信息
          select标签用于查询的标签
             id:标签的唯一标识
             resultType: 定义返回的类型 把sql查询的结果封装到哪个实体类中
         #{id}===表示占位符等价于?  这是mybatis框架的语法
    -->

    <select id="findAll" resultType="com.wzh.entity.User">
        select * from t_user
    </select>
    <select id="findOne" resultType="com.wzh.entity.User">
        select * from t_user where id=#{id}
    </select>
    <select id="findByNameAndPwd" resultType="com.wzh.entity.User">
        <!--
            根据账号和密码查询用户信息
          mybatis默认会把多个参数封装成param1 param2 .......
          这种方式名称不见名知意
          如果想自己起名称使用@Param
        -->
        select * from t_user where username=#{username} and password=#{password}
    </select>

</mapper>

注意: namespace必须和dao接口一样,而且标签的id必须和你接口的方法名一样

<!--namespace必须和接口的全路径匹配-->

<!--id名称必须和接口中方法名一样-->

 (3)测试

import com.wzh.dao.UserDao;
import com.wzh.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 org.junit.Test;

import java.io.Reader;
import java.util.List;

/**
 * @ProjectName: mybatis0601_1
 * @Package: PACKAGE_NAME
 * @ClassName: Test01
 * @Author: 王振华
 * @Description:
 * @Date: 2022/6/1 18:57
 * @Version: 1.0
 */
public class Test01 {
    @Test
    public void testFindAll() throws Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class); //获取相应接口的代理对象
        List<User> list = userDao.findAll();
        System.out.println(list);
        session.commit();
        session.close();
    }
    @Test
    public void testFindOne() throws Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);//获取相应接口的代理对象
        User user = userDao.findOne(1);
        System.out.println(user);
        session.commit();
        session.close();
    }


    @Test
    public void testInsert() throws  Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        User user = new User("wzh", "123456", "王振华");
        System.out.println(user);
        int row = userDao.insert(user);
        System.out.println(user);
        session.commit();
        session.close();
    }
    @Test
    public void testDelete() throws  Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        int row = userDao.delete(84);
        System.out.println(row);
        session.commit();
        session.close();
    }



    @Test
    public void testUpdate() throws Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        int row = userDao.update(new User(76,"wzh", "123456", "王振华"));
        System.out.println(row);
        session.commit();
        session.close();
    }
    @Test
    public void testFindByNameAndPwd() throws Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        User user = userDao.findByNameAndPwd("wzh", "123456");
        System.out.println(user);
        session.commit();
        session.close();
    }
}

常见的bug

(1)namespace和接口名不对应。 xml文件没有注册到mybatis配置文件中

 (2)映射文件中的标签id和接口中方法名不对应

 1.2.安装mybatis插件

作用: 检查dao和映射文件是否匹配

二者有一即可 

2.传递多个参数

我们在dao接口中某些方法可能需要传递多个参数,譬如: 登录(username,password)

 我们需要在参数处使用@Param()为参数起名。

3.添加时如何返回递增的主键值。

需要返回添加数据库后的id值。

keyProperty必须时UserDao参数的主键  如果参数是Map   需要map.id

   @Test
    public void testInsert() throws  Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        UserDao userDao = session.getMapper(UserDao.class);
        User user = new User("wzh", "123456", "王振华");

        System.out.println(user);    //id=null

        int row = userDao.insert(user);
    
        System.out.println(user);    //id有值
        
        session.commit();
        session.close();
    }

4.解决字段名与实体类属性名不相同的冲突

问题: 查询时返回一个null,或者某个列没有值

可以有两种解决办法:

第一种: 为查询的列起别名,而别名和属性名一致。

<mapper namespace="com.wzh.dao.StudentDao">
    <!--查询所有-->
    <select id="findAll" resultType="com.wzh.entity.Student">
        select stu_id stuId,stu_name stuName,stu_sex stuSex,stu_age stuAge,stu_salary stuSalary from student
    </select>
</mapper>

 第二种: 使用resultMap完成列和属性之间的映射关系。

<mapper namespace="com.wzh.dao.StudentDao">

 <!--resultType和ResultMap二者只能用一个-->
<select id="findAll" resultMap="stu">
        select * from student
    </select>
<resultMap id="stu" type="com.wzh.entity.Student">
        <!--主键的映射关系 column:列名 property:属性名-->
        <id column="stu_id" property="stuId"/>
        <!--普通列的映射关系-->
        <result column="stu_name" property="stuName"/>
        <result column="stu_sex" property="stuSex"/>
        <result column="stu_age" property="stuAge"/>
        <result column="stu_salary" property="stuSalary"/>
    </resultMap>

</mapper>

resultMap的id必须和select里的resultMap一致

如果列名和属性名有些一致的,可以在resultMap中不写映射关系,不需要全部都写,不一致的写就行

5. 动态sql

5.1什么是动态sql

根据参数的值,判断sql的条件。

name!=null address!=null
select * from 表名 where name=#{name} and address=#{address}
name==null
select * from 表名 where address=#{address}

5.2.为什么要使用动态sql

 

5.3.mybatis中动态sql标签有哪些

 

 5.3.1.if标签--单条件判断

//如果stuAge不为null则按照stuAge查询 如果为null则查询所有
public interface StudentDao {
    public List<Student> findAll();
    public List<Student> findByCondition(@Param("stuName") String stuName,@Param("stuAge") Integer stuAge,@Param("stuSalary") Double stuSalary);
}

StudentMapper.xml: 

<select id="findByCondition" resultMap="stu">
        select * from student where 1=1
            <!--符合条件执行  不符合跳过-->
            <if test="stuName!=null and stuName!=''">
                and stu_name=#{stuName}
            </if>
            <if test="stuAge!=null">
                and stu_age=#{stuAge}
            </if>
            <if test="stuSalary!=null">
                and stu_salary=#{stuSalary}
            </if>
        
</select>

    <resultMap id="stu" type="com.wzh.entity.Student">
        <id column="stu_id" property="stuId"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_sex" property="stuSex"/>
        <result column="stu_age" property="stuAge"/>
        <result column="stu_salary" property="stuSalary"/>
    </resultMap>

测试:

 @Test
    public void testFindCondition() throws Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        StudentDao studentDao = session.getMapper(StudentDao.class);
        List<Student> list = studentDao.findByCondition( null,null, null);
        System.out.println(list);
        session.commit();
        session.close();
    }

5.3.2. choose标签 多条件分支判断

   choose就相当于多分支条件语句 类似于java中的switch...case...default

<mapper namespace="com.wzh.dao.StudentDao">
    <select id="findByCondition" resultMap="stu">
        select * from student where 1=1
            
<!--when中有一个条件成立就不会再往下执行,当所有条件都不成立时,就会执行otherwise中的语句-->
            <choose>
                <when test="stuName!=null and stuName!=''">
                    and stu_name=#{stuName}
                </when>
                <when test="stuAge!=null">
                    and stu_age=#{stuAge}
                </when>
                <when test="stuSalary!=null">
                    and stu_salay=#{stuSalary}
                </when>
                <otherwise>
                    and stu_sex=0
                </otherwise>
            </choose>
        
    </select>

    <resultMap id="stu" type="com.wzh.entity.Student">
        <id column="stu_id" property="stuId"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_sex" property="stuSex"/>
        <result column="stu_age" property="stuAge"/>
        <result column="stu_salary" property="stuSalary"/>
    </resultMap>
</mapper>

 测试同上

 5.3.3.where标签

我们观察到上面的sql都加了 where 1=1 ,如果不使用where 1=1 那么你的动态sql可能会出错。 我们能不能不加where 1=1呢! 可以 那么我们就可以使用where标签,作用:可以自动为你添加where关键字,并且可以帮你去除第一个and |or

<select id="findByCondition" resultMap="stu">
        select * from student
        <where>
            <if test="stuName!=null and stuName!=''">
                and stu_name=#{stuName}
            </if>
            <if test="stuAge!=null">
                and stu_age=#{stuAge}
            </if>
            <if test="stuSalary!=null">
                and stu_salary=#{stuSalary}
            </if>
        </where>
</select>

    <resultMap id="stu" type="com.wzh.entity.Student">
        <id column="stu_id" property="stuId"/>
        <result column="stu_name" property="stuName"/>
        <result column="stu_sex" property="stuSex"/>
        <result column="stu_age" property="stuAge"/>
        <result column="stu_salary" property="stuSalary"/>
    </resultMap>

5.3.4.set标签

这个配合if标签一起用,一般用在修改语句。如果传递的参数值为null,那么应该不修改该列的值。

<!--修改-->
    <update id="update">
        update tb_emp
        <!--set:可以帮我们生成关键字  set并且可以去除最后一个逗号-->
        <set>
            <if test="name!=null and name!=''">
                name=#{name},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
            <if test="salary!=null">
                salary=#{salary}
            </if>
            where id=#{id}
        </set>
    </update>

这里我们以tb_emp表为例,属性有id,name,age,job,salary,entrydate,managerid,dept_id,我们只需要关注name,age,salary三列,测试代码如下:

@Test
    public void testUpdate() throws Exception{
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
        SqlSession session = factory.openSession();
        EmpDao empDao = session.getMapper(EmpDao.class);
        int row = empDao.update(new Emp(29, "孙悟空", null, 5000.00));
        System.out.println(row);
        session.commit();//必须提交
        session.close();
    }

因为我们的age属性为null,所有sql语句为:

 5.3.5.foreach标签

循环标签,可以帮助我们处理一些动态的批量任务,例如批量添加,批量查询,批量删除等等。我们可以传入一个数组或者一个list集合,然后通过foreach标签将集合内的条件一个一个的放入我们的sql语句从而实现动态的批量处理效果。

foreach标签内属性:

collection:类型  如果你使用的为数组array  如果你使用的为集合 那么就用list
item:数组中每个元素赋值的变量名
 open: 以谁开始
close:以谁结束
separator:分割符
        这里以tb_emp表为例:

 (1)根据多个id查询多条记录(批量查询)

EmpDao:

EmpMapper.xml :

    <sql id="empMap">
       id,name,age,job,salary,entrydate,managerid,dept_id
    </sql>
    <!--根据多个id查询多条记录-->
    <select id="selectByIds" resultMap="eMap">
        <!--
         如果你使用的为数组array  如果你使用的为集合 那么就用list
        collection:类型
        item:数组中每个元素赋值的变量名
        open: 以谁开始
        close:以谁结束
        separator:分割符
        -->
        select <include refid="empMap"/> from tb_emp where id in
        <foreach collection="array" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </select>
    <resultMap id="eMap" type="com.wzh.entity.Emp">
        <result column="dept_id" property="deptId"/>
    </resultMap>

(2)根据多个id删除多条数据(批量删除)

EmpDao:

EmpMapper.xml

 <!--批量删除-->
    <delete id="delete">
        delete from tb_emp where id in
        <foreach collection="array" item="id" separator="," open="(" close=")">
            #{id}
        </foreach>
    </delete>

(3)批量添加学生信息

        这里我们传入的参数用List<Emp>集合。集合里面装的都是Emp对象类型的数据。这样才能将要添加的学生的信息传入到sql中。

EmpDao:

 EmpMapper.xml:

注意,我们要想调用list集合内的Student对象的属性,调用的写法应该为“Xxx(item名称).xxx(属性名)”

    <insert id="insert">
        insert into tb_emp values
        <foreach collection="list" item="emp" separator=",">
            (null,#{emp.name},#{emp.age},#{emp.job},null,null,null,null)
        </foreach>
    </insert>

5.3.6.trim标签

trim元素可以帮助我们去掉一下and、or等,prefix代表语句前缀, prefixOverrides代表要去掉的字符串

    <select id="selByChoose" resultType="Account">
        select id,name,created,updated,money from account
        <trim prefix="where" prefixOverrides="and">
        <choose>
            <when test="name !=null and name !=''">
                and name like concat('%',#{name},'%')
            </when>
            <when test="money !=null and money !=''">
                and money =#{money}
            </when>
            <otherwise>
                and isdeleted=1
            </otherwise>
        </choose>
        </trim>
    </select>

5.3.7.sql片段

在执行查询语句时不建议大家使用select *, 建议大家把查询的列写出。

定义sql片段,里面包含查询的列名

 6.mybatis映射文件处理特殊字符

在Mapper中写我们的sql语句中,会遇到一些特殊的字符,他会与我们的sql代码会产生冲突,例如我们sql中写这样的语句:

        可以看到我们的小于号,就是一种特殊字符。那么我们该如何处理这种问题?

 有两种方法可以解决。

第一种:使用转义符。java中有这些特殊字符的转义符。

 

 第二种:用   <![CDATA[sql语句]]> 方法 注意这种方法不能括住我们的标签(例如include标签),我们可以在需要用到的地方使用这个方法,没必要整个sql语句都套入在内。

 7.mybatis完成模糊查询

  模糊查询的sql语句:

select * from 表名 where 列名 like '%a%'

(1)使用字符串函数 完成拼接 -----concat(这属于sql基础的知识点)

    <select id="selectByName" resultMap="eMap">
        select <include refid="emp"/> from tb_emp where name like concat('%',#{name},'%')
    </select>

(2) 使用${ } 代替。 这种方式实际上是字符串的拼接,他并不能防止sql注入问题,而我们之前用的#{ }就相当于预编译,可以防止sql注入问题,#{}实际使用的PreparedStatement。所有这种方式不推荐使用,所谓防君子不防小人。

    <select id="selectByName" resultMap="eMap">
           select <include refid="emp"/> from tb_emp where name like '%${name}%'
    </select>

8.联表查询

我们上边的操作,都是单表操作。实际开发中也会碰到联表操作。

8.1.一对一

       例子:根据id查询员工信息并包括该员工所在的部门信息。

        需要用的表:我们需要从多的一方来查询一的一方。这里员工表就相当于多的一方,部门表就相当于1的一方。

 sql语句: 

select * from tb_emp e join tb_dept d on e.dept_id=d.id where e.id=×××

实现方式一:

        首先在我们的Emp实体类中(相当于多的一方)加入我们Dept类型(一的一方)的属性

package com.wzh.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @ProjectName: mybatis0602_2
 * @Package: com.wzh.entity
 * @ClassName: Student
 * @Author: 王振华
 * @Description:
 * @Date: 2022/6/2 16:32
 * @Version: 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer id;
    private String name;
    private Integer age;
    private String job;
    private Double salary;
    private Date entrydate;
    private Integer managerid;
    private Integer deptId;
    private Dept dept;  //Dept类型的属性
}



package com.wzh.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @ProjectName: mybatis0602_2
 * @Package: com.wzh.entity
 * @ClassName: Dept
 * @Author: 王振华
 * @Description:
 * @Date: 2022/6/2 16:34
 * @Version: 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {
    private Integer id;
    private String name;

}

EmpDao:

EmpMapper.xml:

 重点:resultMap标签内的association标签 :表示 一 的一方。

                                        property:表示属性名(就是我们实体类种的aclass属性)。

                                        javaType:表示该属性名对应的数据类型。 

    <sql id="empAndDept">
        e.id,e.name,e.age,e.job,e.salary,e.entrydate,e.managerid,e.dept_id,d.id did,d.name dname
    </sql>
    <!--autoMapping   自动映射-->
    <resultMap id="empMap" type="com.wzh.entity.Emp">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="age" property="age"/>
        <result column="job" property="job"/>
        <result column="salary" property="salary"/>
        <result column="entrydate" property="entrydate"/>
        <result column="managerid" property="managerid"/>
        <result column="dept_id" property="deptId"/>
        <!--association: 表示一的一方
                property: 它表示属性名
                javaType: 该属性名对应的数据类型
          -->
        <association property="dept" javaType="com.wzh.entity.Dept">
            <id column="did" property="id"/>
            <result column="dname" property="name"/>
        </association>
    </resultMap>
    <select id="findById" resultMap="empMap">
        select <include refid="empAndDept"/> from tb_emp e join tb_dept d on e.dept_id=d.id where e.id=#{id}
    </select>

实现方式二:

       map封装(不推荐这种方式)。用这种方式封装我们查询到的数据,就不需要改动我们的实体类,也不用关心其属性内的映射关系。

//根据员工编号查询员工信息以及部门信息
 public Map findById(Integer id);

EmpMapping.xml:

<!--key:value-->
    <select id="findById" resultType="java.util.Map">
        select <include refid="empAndDept"/> from tb_emp e join tb_dept d on e.dept_id = d.id where e.id=#{id}
    </select>

    需要避免的问题:两个表的列名应该尽量避免相同,如果相同,需要起别名来区别。resultMap中的标签内的column属性应该与你起了别名后的列名相同。Map本身也是无序不重复的,我们的属性名也就是map中的key值也需要保证不能相同。

sql片段里面是可以起别名的,不过要与sql语句的别名一致.

8.2.一对多

 

 

 

 

 

总结

1. 实际开发创建一个dao接口和映射文件。---namespace要和dao接口相同,id要和方法名相同。
2. 传递多个参数----@Param("名称") == xml #{名称}
3. 添加时需要获取递增的主键id.
4. 列名和属性名不一致。---别名    ===resultMap
5. 动态sql. 
     if
     choose
     where

     set

     foreach

     sql片段

     trim

6.mybatis映射文件处理特殊字符有两种解决方法:(1)转义字符  (2)<![CDATA[SQL语句]]>

7.mybatis完成模糊查询  (1)拼接字符串concat('%',#{内容},'%')  (2)${} 不能防止sql注入

8.联表查询   (1)在一的实体类里面添加多的对象属性 (2)map封装   不推荐

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值