MyBatis---sql映射文件

增删改查

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 示例:增删改查示例
  • 实体类:Employee
@Alias("employee")//别名
public class Employee {
    private Integer id;
    private String ename;
    private Integer age;
    private String job;
    ....
}
  • EmployeeDAO接口
package mao.shu.dao;

import mao.shu.vo.Employee;

public interface EmployeeDAO {
    /**
     * 根据雇员id查询单个雇员信息
     * @param id
     * @return
     */
    public Employee getEmpById(Integer id);

    /**
     * 增加雇员操作
     * @param vo
     * @return
     */
    public Integer addEmp(Employee vo);

    /**
     * 修改一个雇员信息
     * @param vo
     * @return
     */
    public boolean updateEmp(Employee vo);

    /**
     * 删除一个雇员信息
     * @param id
     * @return
     */
    public boolean removeEmp(Integer id);
}

  • 使用MySQL数据库编写对应的数据表

在这里插入图片描述

  • 编写sql映射文件—EmployeeMapper.xml
<?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="mao.shu.dao.EmployeeDAO">
    <select id="getEmp" resultType="mao.shu.vo.Employee">
        select id, ename, age, job
        from employee
        where id = #{id}
    </select>

    <!--设置添加操作的sql语句
        ,useGenerateKeys="true" 表示自动增加主键值
        keyProperty="id" 表示主键对应的属性
    -->
    <insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO employee(ename, age, job)
        VALUES (#{ename}, #{age}, #{job})
    </insert>

    <update id="updateEmp">
        UPDATE employee
        SET ename=#{ename},
            age=#{age},
            job=#{job}
        WHERE id = #{id}
    </update>

    <delete id="removeEmp">
        DELETE
        FROM employee
        WHERE id = #{id}
    </delete>
</mapper>
  • 设置MyBatis全局映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="properties/datasource.properties"/>
    <environments default="mysql">
        <!--定义开发时的环境配置
            链接mysql数据库
        -->
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

    </environments>
    <databaseIdProvider type="DB_VENDOR">
        <property name="MySql" value="mysql"/>
        <property name="Oracle" value="oracle"/>
    </databaseIdProvider>

    <!--注册sql-->
    <mappers>
        <mapper resource="config/EmployeeMapper.xml"/>
    </mappers>
</configuration>


  • 编写测试类
package mao.shu.dao;

import mao.shu.vo.Employee;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

import static org.junit.Assert.*;

public class EmployeeDAOTest {
    private SqlSession sqlSession;
    @Before
    public void before() throws IOException {
        String resource = "config/mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        this.sqlSession = sqlSessionFactory.openSession();
    }

    @After
    public void after(){
        this.sqlSession.commit();
        this.sqlSession.close();
    }

    @Test
    public void testSelect(){
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Employee emp = employeeDAO.getEmpById(1);
        System.out.println(emp);
    }

    @Test
    public void testUpdate(){
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Employee emp = employeeDAO.getEmpById(1);
        emp.setEname("测试数据");
        boolean flag = employeeDAO.updateEmp(emp);
        System.out.println(flag);
    }
    @Test
    public void testInsert(){
        Employee emp = new Employee();
        emp.setEname("添加测试");
        emp.setAge(33);
        emp.setJob("测试员");
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Integer result = employeeDAO.addEmp(emp);
        System.out.println(result);
    }

    @Test
    public void testRemove(){
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        boolean flag = employeeDAO.removeEmp(2);
        System.out.println(flag);
    }

}
  • 后台输出

在这里插入图片描述

  • 生成的数据表

在这里插入图片描述

获取自增主键的值

  • 要使用自增主键,首先在创建数据表的时候,就需要设置主键自增
  • 只有数据库支持自动生成主键(例如:MySQL,SQL Server等数据库)

在这里插入图片描述

  • 如果使用命令行方式创建数据表

create table (
id int PRIMARY KEY AUTO_INCREMENT
)

在这里插入图片描述

  • 示例:设置获取主键自增值
  • sql映射文件
    <!--设置添加操作的sql语句
        ,useGenerateKeys="true" 表示自动增加主键值
        keyProperty="id" 表示将自增的主键值赋给对应的java属性
    -->
    <insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO employee(ename, age, job)
        VALUES (#{ename}, #{age}, #{job})
    </insert>
  • 测试
@Test
    public void testInsert(){
        Employee emp = new Employee();
        emp.setEname("添加测试");
        emp.setAge(33);
        emp.setJob("测试员");
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Integer result = employeeDAO.addEmp(emp);
        
        Integer id = emp.getId();
        System.out.println("新增雇员id "+id);
    }

在这里插入图片描述

Oracle使用序列生成主键值

  • Oracle不支持直接生成主键,在Oracle中使用序列的方式代替了自增主键
  • Oracle中创建序列的语法:
CREATE SEQUENCE name --name为序列名称

[INCREMENT BY n] --可选值 序列每次步增 n 个单位

[START WITH n]              --设置序列以 n 开始

[{MAXVALUE n | NOMAXVALUE}] --设置最大值

[{MINVALUE n | NOMINVALUE}] --设置最小值

[{CYCLE | NOCYCLE}] --是否循环

[{CACHE n | NOCACHE}] --是否启用缓存
  • 使用序列创建数据
  • 序列的使用
  • currval 表示序列的当前值,新序列必须使用一次nextval 才能获取到值,否则会报错
  • nextval 表示序列的下一个值。新序列首次使用时获取的是该序列的初始值,从第二次使用时开始按照设置的步进递增
  • 示例:
insert info tablename(id,feld) values(sequenceName.nextval,field)

使用selectKey获取主键值

在这里插入图片描述

  • 示例:先查询序列,在添加数据

    <insert id="addEmp" databaseId="oracle">
        <!-- 设置查询主键值的sql语句
            order 属性控制查询主键sql是在插入sql语句之前执行还是之后执行
                BEFORE : 之前执行
                AFTER : 之后执行
             在执行添加语句之前,会限制性<selectKey>标签中的sql语句,
             而后将查询出来的数据赋值给 keyProperty 对应的类属性
        -->
        <selectKey keyProperty="id" resultType="integer" order="BEFORE">
            SELECT EMPLOYEE_SEQ.nextval
            FROM dual
        </selectKey>
        INSERT INTO EMP(empno,ename, job)
        VALUES (#{id},#{ename},#{job})
    </insert>
  • 测试代码:
    @Test
    public void testInsert(){
        Employee emp = new Employee();
        emp.setEname("oracleTest");
        emp.setJob("测试员");
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Integer result = employeeDAO.addEmp(emp);
        Integer id = emp.getId();
        System.out.println("新增雇员id "+id);
    }
  • 测试结果

在这里插入图片描述

在这里插入图片描述

  • 示例:先插入数据,再查询序列值
  • Oracle中使用序列可以使用序列.nextval的方式直接获取序列下一个值,使用这样的方式插入数据,而后再使用"序列.currval"的方式取得刚刚插入的序列值.
  • 在sql映射文件中支持使用<selectKey>标签中的order属性,控制查询序列值执行顺序.
  • 这样的方式非常的不安全,可能会导致得到的序列值并非最新的,因此不建议使用
  <insert id="addEmp" databaseId="oracle">
<!-- 在添加数据之后,查询序列值-->
        <selectKey keyProperty="id" resultType="integer" order="AFTER">
            SELECT EMPLOYEE_SEQ.currval
            FROM dual
        </selectKey>
        INSERT INTO EMP(empno,ename, job)
        VALUES (EMPLOYEE_SEQ.nextval,#{ename},#{job})
    </insert>

在这里插入图片描述

在这里插入图片描述

参数处理

在这里插入图片描述

处理单个参数

  • #{参数名}:此时mybatis不会做任何处理,参数名可以任意取.
  • 示例:
    <select id="getEmpById" resultType="mao.shu.vo.Employee">
        select id, ename, age, job
        from employee
        where id = #{id}
    </select>
  • 测试方法
    @Test
    public void testSelect(){
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Employee emp = employeeDAO.getEmpById(1);
        System.out.println(emp);
    }

在这里插入图片描述

处理多个参数

  • 多个参数会被封装成一个map,
    • key=param1,param2,或者为map索引0,1
    • value=传入的参数值
  • 而"#{}"就是从map中获取指定的key的值

在这里插入图片描述

  • 示例:接口中定义的查询方法
    public Employee getEmpByIdAndName(Integer id,String ename);
  • 定义sql映射文件
    <select id="getEmpByIdAndName" resultType="mao.shu.vo.Employee">
        select id, ename, age, job
        from employee
		 where id = #{0} AND ename=#{1}
    </select>
  • 测试方法
    @Test
    public void testgetEmpByIdAndName(){
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Employee emp = employeeDAO.getEmpByIdAndName(1,"测试数据");
        System.out.println(emp);
    }

命名参数

  • 明确指定封装参数是map的key
  • 使用@Param注解修饰方法入参
  • 修改接口定义的查询方法
public Employee getEmpByIdAndName(@Param("id") Integer id, @Param("ename") String ename);
  • 修改sql映射文件
    <select id="getEmpByIdAndName" resultType="mao.shu.vo.Employee">
        select id, ename, age, job
        from employee
        where id = #{id} AND ename=#{ename}
    </select>

POJO和Map

  • 如果多个参数正好是业务逻辑的数据模型,就可以直接传入POJO类对象.
    public Integer addEmp(Employee vo);
   <insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO employee(ename, age, job)
        VALUES (#{ename}, #{age}, #{job})
    </insert>
  • 如果多个参数不是业务模型中的数据,没有对应的pojo,为了方便,我们也可以传入Map对象.
  • 此时"#{}"取出的就是map中对应的值
  • 示例:接口方法定义
public Integer addEmpByMap(Map<String,Object> map);
  • sql映射文件
    <insert id="addEmpByMap">
        INSERT INTO employee(ename, age, job)
        values (#{ename}, #{age}, #{job})
    </insert>
  • 测试方法
    @Test
    public void testAddByMap(){
        Map<String,Object> map = new HashMap<>();
        map.put("ename","map添加测试");
        map.put("age",33);
        map.put("job","程序员");
        EmployeeDAO employeeDAO = this.sqlSession.getMapper(EmployeeDAO.class);
        Integer result = employeeDAO.addEmpByMap(map);
        System.out.println(result);
    }

在这里插入图片描述

  • 如果多个参数不是业务模型中的数据,但是经常要使用,直接推荐来编写一个TO(Transfer Object) 数据传输对象.
  • 例如,如果经常使用到分页功能,则可以将分页的基本数据包装为一个Page类
public class Page{
	private Integer currentPage;
	private Integer linesize;
}

参数封装扩展思考

  • 当接口方法中定义了多个参数,但是其中包含POJO类的参数,此时MyBatis会如何处理?
  • 例如:
  • 其中参数id是单个的参数
  • 而emp对应的类描述的是雇员表的信息
public void select(Integer id,Employee emp)
  • 那此时如果要在sql映射文件中获取方法的参数,因为两个参数都没有使用 @Param 注解修饰,所以MyBatis会默认将两个参数保存到Map集合中.
  • 所以如果要取得第一个id参数内容则要使用 “#{param1}”,或者"#{0}的方式
#{param1}
#{0}
  • 而如果要取得第二个参数"Employee"对象的lastName成员变量,则需要使用以下方式
#{param2.lastName}
  • 特别注意:如果是Collection(List、Set)类型或者是数组也会特殊处理。也是把传入的list或者数组封装在map中。
  • key:Collection(collection),
  • 如果是List还可以使用这个key(list)
  • 数组(array)
public Employee getEmpById(List<Integer> ids);
取值:取出第一个id的值:   #{list[0]}

参数封装map的过程

  1. 获取每个标了param注解的参数@Param的值,id,lastName,赋值给Name
  2. 每次解析一个参数给map中保存信息(key:参数索引,value,name的值)
public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    //1、参数为null直接返回
    if (args == null || paramCount == 0) {
      return null;
     
    //2、如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
      
    //3、多个元素或者有Param标注
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      
      //4、遍历names集合;{0=id, 1=lastName,2=2}
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
      
      	//names集合的value作为key;  names集合的key又作为取值的参考args[0]:args【1,"Tom"】:
      	//eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
        param.put(entry.getValue(), args[entry.getKey()]);
        
        
        // add generic param names (param1, param2, ...)param
        //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
        //效果:有Param注解可以#{指定的key},或者#{param1}
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}

#和$取值的区别?

  • #{}:是以预编译的形式,将参数设置到sql语句中:PreparedStatement
  • ${}:取出的值直接拼装在sql语句中,会有安全问题
  • 大多情况下:取参数的值都应该是用#{},但是有些时候,对于原生不支持占位符的情况就可以使用.
  • 例如:sql语句中的表名字段无法使用占位符的方式设置参数
    <select id="getEmpByIdAndTableName" resultType="mao.shu.vo.Employee">
        SELECT id, ename, age, job
        FROM ${tableName}
        WHERE id = #{id}
    </select>
  • 例如:排序的字段可以使用${}来拼凑sql语句

    <select id="getAll" resultType="mao.shu.vo.Employee">
        SELECT *
        FROM employee
        ORDER BY ${column}
    </select>

#{}取值时规定参数规则

在这里插入图片描述

在这里插入图片描述

  • 默认情况下如果参数作为"null"值被传入那么MyBatis会默认将其转换为"Type.Outher"类型,而Oracle数据库无法识别"Type.Other"类型,所以当使用Oracle的时候就可能会出现错误

在这里插入图片描述

  • 这个时候可以在sql语句中取出参数时,设置jdbcType 属性,值为"NULL"大写
INSERT INTO EMP(empno,ename, job)
VALUES (EMPLOYEE_SEQ.nextval,#{ename,jdbcType=NULL},#{job,jdbcType=NULL})
  • 在JdbcType类中定义类许多JDBC的类型

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值