Mybatis概述

143 阅读11分钟
			Mybatis框架学习

学习介绍:

Mybatis是持久层框架:它针对的是jdbc的优化;使用它可以让数据库的操作变得简单;提高持久层数据处理的效率;

零:Mybatis简介

一:Mybatis基本使用:
     对数据库单表的增删改查
     
二:Mybatis多表映射:
     
三:Mybatis动态语句

四:Mybatis高级扩展

五:总结
    
    主要记忆api+配置即可

零:Mybatis简介:

Mybatis是持久层框架,可以简化数据库操作,但是需要我们自己写sql语句;

一:Mybatis快速入门:

步骤: 
1.导入依赖
 2.准备实体类(相关数据库信息)
 3.创建一个mapper接口:(用来规定方法)
   mapperxml文件:(用来写 方法对应的实现:专注写sql语句)
 4.准备mybatis配置文件:
     里面:链接数据库信息
           mapper.xml文件的位置(最终api读的是mybatis配置文件)
           .....
 5.使用mybatis的api进行数据库查询即可
    :SqlSessionFactory:会缓存配置的所有信息;哪个业务想要,就根据它创建一个SqlSession对象要获取mapper接口的代理对象,
     之后:SqlSession对象要根据getMapper(接口.class)来获取mapper接口的代理对象,去找去找对应的sql语句
     然后通过 "代理对象.方法"就可以进行数据库操作了;

二:Mybatis基本使用

(1)向SQL语句传参:

1.mybatis日志输出配置:

Mybatis日志配置:

 <settings>
        <!--!!!!!开启了Mybatis的日志输出,选择使用system进行日志进行控制台输出-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

2.接收输入参数的细节:

方法外部传进来一个id值,要在这动态取到id值; 方式有两种:
        a:“ #{key} ”:占位符+ 赋值 eg:第一步:emp_id=? 第二步:?=赋值
        b:${key} :字符串拼接 eg:emp_id="" 然后将id与它拼接 
        
      推荐使用第一种,防止注入攻击;
      注意:!占位符?,只能替代值的位置,不能替代 容器名(表名、列名,sql关键字)
      
      使用第二种场景:一般用来拼接列名 表名;
        eg:当我们需要:select * from 表 where 列名(列名是动态的) =动态的值
          这时:不能使用第一种:因为占位符?,不能替代列名;
          所以这时我们使用第二种;
          
      总结:动态值:使用:#{};
           动态列名、容器名、关键字:${}

(2)数据输入:

外部接口中的抽象方法传入的各种数据类型:如何在{}内定义key;
   
    
一:
 (1)单个简单类型参数:eg:int / double / String等
    
     eg:1.1
     <!--
    场景1:传入的是单个简单类型:在{}内部如何接值: 
         key随便写,但是一般写参数名;
    -->
    // 1.单个简单类型参数:
    //根据id删除员工id信息:(DML语句:增删改查返回全是int)
    int deleteById(Integer id);


    //根据工资查询员工信息:
    List<Employee> queryBySalary(Double salary);
    
   如何在{}内接值:
    <delete id="deleteById">
        delete from t_emp where emp_id=#{ergz}
    </delete>
    
 (2)单个实体类型参数:{}内部的key如何写
        
         //2.单个实体类型参数:
    //插入员工数据:一般情况员工数据:是“ 实体对象 ”
    //如何在{}内部取值
    int insertEmp(Employee employee);

  <!--场景二:传入的是实体类对象:key如何写:
     key就=实体类的属性名即可;(实体类中定义的属性名)
    -->

    <insert id="insertEmp">
        insert into t_emp (emp_name,emp_salary) value(#{empName},#{empSalary});
        <!--1.因为是值:所以使用#{}; 2.-->
    </insert>
</mapper>
        
        
 (3)多个简单类型传入,key如何写:  
            //3.传入多个简单类型参数:
    //根据员工姓名和工资查询员工信息:
    //使用List:可能有重名员工
    List<Employee> queryByNameAndSalary(@Param("a") String name, @Param("b") Double salary);
            
            
            
 <!--场景3:
        传入多个简单类型数据如何取值 :key如何写
         方案1(常用):注解指定:@param注解:用来指定多个简单参数的key; ->:key=@Param("value值")
         方案2:mybatis默认机制:
               a:arg0 arg1...:形参从左到右依次对应 arg0 arg1...
                (name,salary) name-> key=arg0 salary=arg1
               b:param1 param2...
                (name,salary) name->key=param1 salary-> key=param2
    -->

    <select id="queryByNameAndSalary" resultType="com.atguigu.pojo.Employee">
        select emp_id empId ,emp_name empName,emp_salary empSalary
        from t_emp where emp_name= #{a} and emp_salary=#{b}
        <!--方案2:#{arg0}  #{arg1}-->
    </select>
            
            
 二:复杂类型:多值类型 eg:实体类对象、map、list
     
     (1)map类型:

    //4.map:
    //插入员工数据:但是员工数据装在map里面:map(name=员工的名字,salary=员工的薪水)
    int insertEmpMap(Map data);


    <!--场景4:传入map如何指定key的值
    key=map的key即可;
    -->
    <insert id="insertEmplMap">
        insert into t_emp (emp_name,emp_salary) value(#{name},#{salary});
    </insert>

(3)数据输出:

数据输出主要有两种形式:
  1.对应的标签中写的是DML语句(增删改):
    不需要我们指定输出类型,默认返回值就是int,我们可以在外部可以直接使用int/long接值;
    
  2.SQL语句查询:(主要)
   我们需要在 标签的 "resultType"指定输出对应的类型;

!!!即在进行查询时:根据我们数据输出/返回 的 数据类型:应如何指定resultType
  

1.单个简单类型



//(1)如果我们deml语句:插入/修改/删除:
     /*
     返回值:是int/long
      */
    int deleteById(Integer id);
    
     <delete id="deleteById">
        delete from t_emp where emp_id=#{id}
    </delete>
    
    
    
    //(2)需要我们指定输出类型的:一般为查询语句:
    //需求:根据员工的id查询员工的姓名:
     String queryNameById(Integer id);

     //根据员工的id查询员工的工资
    Double querySalaryById(Integer id);
    
    
     <!--
    输出场景1:查询的是:单个简单类型,返回单个简单类型,如何指定resultType:(即如何指定返回值的类型);
       resultType语法:
          1.类的全限定符合:java.lang.String 、java.lang.Double
          2.别名简称:string、double;
              Mybatis给我们提供了72种默认的别名!:这些都是我们常用的Java数据类型;
                eg:如果是基本数据类型是:intdouble:->:_int _double
                   如果是包装数据类型:Integer、Double:->小写即可:int/integer  double
                   如果是集合/容器类型:Map、List、HashMap:->小写即可...
              如果没有提供的需要我们自己定义或者 写 类的全限定符号

           扩展:自定义的类如何定义别名:在mapper.xml:添加标签 <typeAliases>
             <typeAliases>

           a:单独定义:有一个自定义类:就在配置文件中:“ 单独 ”定义一个:<typeAliases>标签
           <typeAlias type="com.atguigu.pojo.Employee" alias="ergouzi"></typeAlias>

           b:批量定义:会将这个pojo包下:批量将包下的所有的类都给与别名:别名就是类名的首字母小写
           之后我们在写的时候:别名可以直接写:类名的首字母小写;
              <package name="com.atguigu.pojo"/>

             </typeAliases>


    -->
    <select id="queryNameById" resultType="java.lang.String">
        select emp_name from t_emp where emp_id=#{id}
    </select>

    <select id="querySalaryById" resultType="java.lang.Double">
        select emp_name from t_emp where emp_id=#{id}
    </select>
        
    总结:DML不需要指定返回类型,只有查询语句需要指定类型:
               第一种:类的全限定符
               第二种:别名;

2.返回实体类对象

//(3.)返回单个自定义实体类;:根据id查询员工信息:
    Employee queryById(Integer id);
    
    
    
     场景二:返回单个自定义类型:
       返回自定义类型:可以写类的全限定符、也可以写别名;
       注意:a:为什么我们在查询的时候 需要起别名:
          如果我们是查询 返回单个实体类型,要求:列名和实体类的属性名要一致!
          这样才可以进行实体类的属性的映射
            b:因为数据库是:蛇形命名,而java是驼峰命名
               但是可以进行设置,设置支持驼峰自动映射;
               数据库:emp_id  java:empId:
               只要符合数据库的蛇形命名,java的驼峰命名,这样就可以自动映射了;



    -->

    <select id="queryById" resultType="com.atguigu.pojo.Employee">

3.返回map类型

4)返回map类型:
  场景:如果想要返回多个值:eg:员工id,工资,平均工资;但没有实体类,我们可以使用map接值
        注意:列名就是对应的key,
             最终查询到的结果就会放到map的值中;
             
           
    /*
    查询部门的最高工资和平均工资
     */
接口:
    Map<String,Object>selectEmpAndMaxSalary();
    
    
    
mapper.xml文件中:
     <!--场景三:
    返回map类型:
     查询的时候没有实体类,但我们还想要保留key:可以使用map接收数据;
     key->就是查询的列;
     value-> 查询的值
    -->
    <select id="selectEmpAndMaxSalary" resultType="map">
        SELECT
        emp_name 员工姓名,
        emp_salary 员工工资,
        (SELECT AVG(emp_salary) FROM t_emp) 部门平均工资
        FROM t_emp WHERE emp_salary=(
        SELECT MAX(emp_salary) FROM t_emp
        )
    </select>

4.返回集合类型:

5)返回集合类型如何指定:
a: 接口:
  	 //(5)返回集合类型:
    //查询工资高于传入值的 员工姓名:分析:一个员工姓名是:String; 多个是:List<String>
    List<String> queryNameBySalary();

    //查询数据库中全部员工信息; 分析:一个员工信息:为Employee对象,多个为List<Object>
    List<  Employee> queryAll();
    
 b:mapper.xml文件:
     <!--
    场景四:返回集合类型
     注意:返回值是集合,:resultType:指定的是:泛型!!!
    -->
    <!--a:-->
    <select id="queryNameBySalary" resultType="string">
        select emp_name from t_emp where salary> #{salary}
    </select>

    <!--b:-->
    <select id="queryAll" resultType="employee">
        select * from t_emp;
    </select>
  
  

5.如何使用mybatis插入并返回自增长的主键

a:接口:
//(6)员工插入:
    int insertEmp(Employee employee);
    
b:mapper.xml文件

    <!--场景5:
      主键回显:获取插入数据的 主键
        1.自增长主键回显:主键是由数据库维护的:auto_increment
             eg:如何在插入的时候:获取自增长主键:即->主键回显
             在mapper.xml文件中:在insert标签中:添加
                  useGeneratedKeys="true":代表:我们想“ 要 ”数据库自动增长的主键值;
                  keyColumn="empId":是主键列的值;(即数据库中哪一列是增长的)
                   keyProperty="empId":接收主键列值的属性!!!(java实体类中的属性;即把主键列值赋给java实体类的属性)
         最终可以在插入的实体类的属性上。取到自增长的值;
        2.
    -->
    <insert id="insertEmp" useGeneratedKeys="true" keyColumn="empId" keyProperty="empId">
        insert into t_emp (emp_name,emp_salary)
          value (#{emp_name} #{emp_salary});
    </insert>

        
c:测试类:
        
    public void test_01() throws IOException {

        //1.读取外部配置文件:mybatis-config:
        InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
        //2.创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
        //3.获取SqlSession对象

        //!!!这句话:opemSession:默认自动开启事务,不会提交,最后需要我们自己提交sqlSession.commit();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        /*
        方法2:!!!!!!!!!!!!!!!!!!
        或者我们使用:openSession(true):自动开启事务,也会自动提交事务,就不需要最后commit了;
         */




        //4.获取代理mapper对象
        //mapper对象能够根据方法和类的信息帮我们调用对应的增删改查方法
        EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);//传入接口

        //使用mapper对象进行增删改查!!!!!!!!!!!!!!!
        Employee employee=new Employee();
        employee.setEmpSalary(888.0);
        employee.setEmpName("二狗子");

        System.out.println(employee.getEmpId());//插入之前:输出主键

        System.out.println("------------------");
        int rows=mapper.insertEmp(employee);

        System.out.println(employee.getEmpId());//插入之后:输出主键
                //最终可以在插入的实体类的属性:EmpId方法上。取到自增长的值:因为将主键增长列赋给了java的empId属性!!!!!!!!!!

        
        System.out.println("rows"+rows);





        //5.释放资源和提交事务:
        //必须提交事务:因为:我们执行了非查询语句,所以最好必须提交事务;要不然不会更新数据库;

        sqlSession.commit(); //上面执行的是DML语句时:必须要手动提交,因为是自动开启事务,必须要提交!!!
        sqlSession.close();

    }


6.非自增长类型主键维护:如何交给mybatis给我们维护

eg:
主键:t_id varchar(64) primary key :是非自增长的主键,在插入数据时,我们每次都要自己插入id,那么我们如何将插入id的步骤交给mybatis呢:


1Teacher接口
     
    public interface TeacherMapper {
        //插入数据,返回的是int:
        int insertTeacher(Teacher teacher);
    }
    
    
2:在实现类中书写方法实现-->
    <mapper namespace="com.atguigu.mapper.TeacherMapper">
    
        <insert id="insertTeacher">
                <!--注意:因为是非自增长类型:t_id varchar(64) primary key
               但是:主键维护太麻烦了,我们可以将非自增长主键交给Mybatis来维护,即我们不想自己赋值,让mybatis自己生成数字,		      然后赋给id!!!
           实现方法:  !!!在<select>标签内部使用: <selectKey>标签!!!!
                   order="BEFORE":指:  a:sql语句是在插入语句之前执行;(还是之后)
                   resultType="string": b:UUID查询返回的类型:字符串:string(别名):
                   keyProperty="":      c:查询的结果:给哪个属性赋值:tId(Teacher的属性名);
              
               -->
                <selectKey order="BEFORE" resultType="string" keyProperty="">
                    select Replace(UUID(),'-','');  <!--uuid:自动生成一个不重复的id-->
                </selectKey>
               这条uuid语句的执行过程:先使用这条sql语句:UUID生成不重复的数字,然后再赋给Teacher的属性tId
               然后我们直接拿着这个属性tId执行insert语句即可!!!!!!!!!

                //insert语句:
                insert into Teacher (t_id,t_name) value (#{t_id},#{t_name});
                <!--因为主键不是自增长:在赋值的时候需要给id也赋值!!!-->

        </insert>

</mapper>


3.测试类:
                    
  @Test
    public void test_02() throws IOException {


        //1.读取外部配置文件:mybatis-config:
        InputStream ips = Resources.getResourceAsStream("mybatis-config.xml");
        //2.创建SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(ips);
        //3.获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //true:代表自动提交:最后就不用手动commit了!!!!!!

        //4.获取代理mapper对象
        //mapper对象能够根据方法和类的信息帮我们调用对应的增删改查方法
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);

        Teacher teacher=new Teacher();//teacher对象
        teacher.settName("hhh");

        //自己维护主键:
        /*String id= UUID.randomUUID().toString().replaceAll("-","");teacher.settId(id);*/

        //我们自己在xml文件中让mybatis维护id!!!!!

        System.out.println(teacher.gettId()); //在执行插入之前:输出Teacher的Id:为“null”;


        //使用获取到的mapper代理对象:mapper进行增删改查操作
        int i=mapper.insertTeacher(teacher);  //插入语句!!!!!!!


        System.out.println(teacher.gettId()); //在执行插入之后:输出Teacher的Id:有了值;

        System.out.println("i"+i);

        //5.关闭资源/提交事务(事务是自动开启的,但不是自动提交)
        sqlSession.close();

    }

两种主键回显小结:

总结:如果是:
     1.自增长主键类型:想要回显:
       <insert id="insertEmp" useGeneratedKeys="true" keyColumn="empId" keyProperty="empId">
                               想要回显                 数据库列名         实体类属性名
                               
       最后:他就会把自增长的主键列名,赋给属性;
       
     如果是:
     2.非自增长的主键:想要回显:交给mybatis去维护
     <select>
         <selectKey order="BEFORE" resultType="string" keyProperty="">
                    select Replace(UUID(),'-','');  <!--uuid:自动生成一个不重复的id-->
         </selectKey>
     </select>