Mybatis总结-基本使用

Mybatis

为什么使用Mybatis?

  • JDBC的缺点
    • SQL夹杂在Java代码块中,耦合度高,修改更新繁琐
    • 维护不容易,实际开发中sql有变化修改困难
  • Hibernate和JPA的缺点
    • Hibernate难处理长难的SQL
    • 内部自动生成的SQL,要做特殊优化困难
    • 基于全映射的自动框架,大量字段的POJO要进行部分映射时比较困难,导致数据库的性能下降
  • Mybatis的优点
    • 可以由开发人员自己优化核心SQL
    • sql和java编码分开,功能边界清晰.一个专注于业务,一个专注于数据

Mybatis的下载地址

  • https://github.com/mybatis/mybatis-3
    Mybatis总结-基本使用
    Mybatis总结-基本使用

Mybatis初步使用

  1. 创建实体类
public class Employee {

    private Integer id;
    private String lastName;
    private String email;
    private String gender;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", email='" + email + '\'' +
                ", gender='" + gender + '\'' +
                '}';
    }
}

  1. 创建Mybatis的全局配置文件
    • 全局配置文件包含了Mybatis运行环境信息,包括数据库的连接信息、事务信息、数据库连接池信息等等,指导这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>
    <!-- 1. mybatis使用properties标签引入外部properties配置文件内容 -->
    <properties></properties>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_study?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="EmployeeMapper.xml"/>
    </mappers>


</configuration>
  1. 创建SQL映射文件 ,映射文件的作用就是相当于定义Dao接口的实现类如何工作
<?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:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
 -->

<mapper namespace="com.wjl.mybatis.dao.EmployeeMapper">
    <select id="getEmpById" resultType="com.wjl.mybatis.bean.Employee">
        select id,last_name lastName,email,gender from tbl_employee where id = #{id}
    </select>
</mapper>
  1. 测试类的实现
    • 不定义接口,直接使用原生的配置文件实现
      public void test01() throws IOException {
            //1. 根据xml配置文件(全局配置文件)创建一个sqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
            // 2. 获取SqlSeeion实例,能直接执行已经映射的SQL语句,一个sqlSession代表和数据库的一次会话
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try{
                // 变量1未xml文件执行的方法,变量2为传入的参数
                Employee employee =  sqlSession.selectOne("com.mybatis.EmployeeMapper.selectEmp",1);
                System.out.println(employee);
            }finally {
                sqlSession.close();
            }
        }
    
    1. 使用接口进行实现,定义相应的接口,映射到Xml文件中进行实现,创建的接口名一定要和xml文件名保持一致,方法名对应着xml文件中每一个增删改查的名字
    // 定义接口类
    public interface EmployeeMapper {
        public Employee getEmpById(Integer id);
    }
    
    <?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:名称空间;指定为接口的全类名
    
     -->
    
    <mapper namespace="com.wjl.mybatis.dao.EmployeeMapper">
        <!-- id为接口中对应的方法名
    		resultType:返回值类型
    		#{id}:从传递过来的参数中取出id值-->
        <select id="getEmpById" resultType="com.wjl.mybatis.bean.Employee">
            select id,last_name lastName,email,gender from tbl_employee where id = #{id}
        </select>
    </mapper>
    
      @Test
    public void test02() throws IOException {
        //1.获取sqlSessionFactory
       String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    
        // 2. 获取SqlSession对象
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            // 3.获取接口的实现类对象
            EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
    
    
            Employee employee = mapper.getEmpById(1);
    
            System.out.println(employee);
        }finally {
            sqlSession.close();
        }
    }
    

注释:SqlSession
SqlSession代表和数据库的一次对话,用完必须关闭
SqlSession和connection都是非线程安全.每次使用都应该去获取新的对象
mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象(将接口和xml进行绑定)

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>
    <!-- 1. mybatis使用properties标签引入外部properties配置文件内容
            resource:引入类路径下的资源
            url:引入网络路径或者磁盘路径下的文件
        -->
    <properties resource="dbconfig.properties"></properties>
    <!-- 2. settings 包含很多重要的设置
            用来设置每一个设置项
    -->
    <settings>
        <!--开启驼峰命名法,默认false -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <!-- 3. typeAliases别名处理器  别名不区分大小写,同时也可以使用@Alias注解在类上为其指定一个别名-->
    <typeAliases>
        <!--typeAlias:为某个java类型起别名
            type:指定要起别名的类型全类名;默认类名就是类名小写;employee
            alia:别名
         -->
        <!--  <typeAlias type="com.mybatis.bean.Employee" alias="emp"></typeAlias>-->
        <!--package:为某个包下的所有类批量起别名
            name:指定包名(为当钱包以及下面所有后代的包每一个类都起一个默认别名)
         -->
        <package name="com.mybatis.bean"></package>

        <!--批量起别名情况下,使用@Alias注解为某个类型指定新的别名 -->
    </typeAliases>
    <!-- 4. plugins -->
    <!--5. environments ,mybatis可以配置多种环境,default指定使用哪种环境
            每一个environment是一个具体的环境信息,通过id来选择环境
            必须配置transactionManager:事务管理器(两种:JDBC,MANAGED),也可自定事务管理器,实现TransactionFactory
                                        type表示事务管理器的类型
            dataSource:数据源(UNPOOLED|POOLED|JNDI)
                        可以自定义数据源,实现DataSourceFactory
    -->
    <environments default="development">
        <environment id="development">
            <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>

    <!-- 6.databaseIdProvider标签 支持多数据厂商
    DB_VENDOR 作用就是得到数据库厂商的标识,mybatis就能根据数据库厂商标识执行不同的sql
    MySQL,Oracle,SQL Server ,xxxx
    在Mapper文件中通过databaseid来指定数据
    -->
    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"></property>
        <property name="Oracle" value="oracle"></property>
    </databaseIdProvider>

    <!-- 7. mappers标签 将sql映射注册到全局配置中 -->
    <mappers>
        <!--mapper :注册一个sql映射
                    resource:引用类路径下的sql映射文件
                    url:引用网络或者磁盘路径下的资源
                    class:引用(注册接口)
                        1.有sql映射文件,映射文件必须与接口同名.并且放到同一个目录下
                        2.没有sql文件,所有的sql都是利用注释写在接口上
         -->
        <mapper resource="EmployeeMapper.xml"/>

        <!-- package 批量注册-->
        <package name="com.wjl.mybatis.dao"></package>
    </mappers>
</configuration>

Mybatis的简单的增删改查

Mybatis的参数传递

  • 单个参数
    • 可以接受基本类型,对象类型,集合类型的值,这种情况Mybatis可以直接使用这个参数,不需要进行任何处理
  • 多个参数
    • 任意多个参数时,会被Mybatis包装成一个Map传入,Map的Key是param1,param2…,值就是参数的值
  • 为参数赋值,可以使用@Param为参数起一个名字,这样使用时key就是我们自己指定的名字
     public Employee getEmpById(@Param("id") Integer id);
    
  • 命名参数
    • 当这些参数属于我们业务POJO时,我们直接传递POJO
  • Map
    • 可以直接封装多个参数为map,直接传递

参数的获取

  • #{} 是以预编译的形式,将参数设置到sql中,防止sql注入

  • ${}取出参数值直接拼装在sql语句中,会有安全问题

  • 大多情况下,我们取参数的值都应该区使用#{}

#{}取值时的用法

  • 可以指定一个特殊的数据类型(jdbcType、javaType,mode(存储过程),numericScale)
    • jdbcType通常需要在某种特定 条件下被设置:在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理.比如Oracle(报错)
    • 全局配置中,jdbcTypeForNull=OTHER,对Orcale无效,可以将jdbcTypeForNull=NULL(在settings标签中)
    • JdbcType Other:无效的类型,因为mybatis对所有null都映射的是原声JDBC Other,Oracle不能正确处理,应该设置为jdbcType=Null

Mybatis的增加操作

  • 简单的增加,返回值可以是int类型代表插入多少行,也可以是boolean代表插入成功失败

        <insert id="addEmp" parameterType="com.mybatis.bean.Employee" >
            insert into tbl_employee(last_name,email,gender)
            values(#{lastName},#{email},#{gender})
        </insert>
    
  • 获取生成的主键id

    • 如果数据库支持自动生成主键(MySQL SQLServer等)则可以设置useGeneratedKeys=“true”,使用自增主键获取主键值策略,keyProperty:指定对应的主键值,也就是mybatis获取到主键值以后,将这个值封装给JavaBean的哪个属性
    <insert id="addEmp" parameterType="com.mybatis.bean.Employee" useGeneratedKeys="true"
                keyProperty="id">
            insert into tbl_employee(last_name,email,gender)
            values(#{lastName},#{email},#{gender})
        </insert>
    
    • 如果数据库不支持自增型主键(比如Oracle),可以使用selectKey子元素,select元素将会首先运行,id会被设置 ,然后插入语句会被调用
      <!--Orcale不支持自增,Orcale使用序列来模拟自增 -->
    <insert id="addEmp" databaseId="orcale">
        <!--keyProperty:查出的主键值封装给javaBean的哪个属性
            order="BEFORE" :当前sql在插入sql之前运行
            resultType 查出的返回值类型
            运行顺序:
                先运行selelctKey查询id的属性,查出id值封装给javaBean的id属性
                在运行插入的sql,就可以取出id的对应值
         -->
        <selectKey keyProperty="id" order="BEFORE" resultType="integer">
            select EMPLOYEES_SEQ.nextval from dual
        </selectKey>
        <!--插入的主键使用序列中拿到的 -->
        insert into tbl_employee(id,last_name,email,gender)
        values(#{id},#{lastName},#{email},#{gender})
    
        <!-- order="AFTER" 方式
                <selectKey keyProperty="id" order="AFTER" resultType="integer">
            select EMPLOYEES_SEQ.currval from dual
        </selectKey>
    
        insert into tbl_employee(id,last_name,email,gender)
        values(EMPLOYEE_SEQ.nextval,#{lastName},#{email},#{gender})
        -->
    </insert>
    

更新操作

  • 同样,上述的获取主键的方法同样适用于更新操作,id需要和方法名保持一致
   <update id="updateEmp">
        update tbl_employee set last_name =#{lastName},email=#{email},gender=#{gender}
        where id = #{id}
    </update>

查询操作

  • select元素用来定义查询操作
    • id为唯一标识符,用来引用这条语句,需要和接口的方法名保持一致
    • parameterType:参数类型,可以不传,Mybatis会根据TypeHandler自动推断
    • resultType:返回值类型,别名或者全类名,如果是集合定义集合中元素的类型,不能喝resultMap一起使用
        <select id="getEmpById" resultType="com.mybatis.bean.Employee">
        select id,last_name,email,gender from tbl_employee where id = #{id}
    </select>
    
  • resultMap,自定义某个JavaBean的封装规则,type 自定义规则的java类型, id:唯一id方便引用,在查询操作中可以指定resultMap来进行封装后的缓存
     <resultMap id="myEmp" type="com.wjl.mybatis.bean.Employee">
            <!--指定主键列的封装规则
            id定义主键会有底层优化
                column:指定哪一列
                property:指定对应的javabean属性
            -->
            <id column="id" property="id"></id>
            <result column="last_name" property="lastName"></result>
            <result column="email" property="email"></result>
            <result column="gender" property="gender"></result>
            <!-- 其他不指定的列会自动封装,我们只要写resultMap就把全部的映射规则全都写上-->
        </resultMap>
    
    • 测试resultMap
    <!--resultMap :自定义结果集规则映射 -->
    <select id="getEmployeeById" resultMap="myEmp">
            select * from tbl_employee where id = #{id}
    </select>
    
resultMap的使用
  • 新建实体类Department

    package com.wjl.mybatis.bean;
    
    import java.util.List;
    
    public class Department {
    
        private Integer id;
        private String departmentName;
        private List<Employee> emps;
    
        public List<Employee> getEmps() {
            return emps;
        }
    
        public void setEmps(List<Employee> emps) {
            this.emps = emps;
        }
    
        public Integer getId() {
            return id;
        }
    
        @Override
        public String toString() {
            return "Department{" +
                    "id=" + id +
                    ", departmentName='" + departmentName + '\'' +
                    '}';
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getDepartmentName() {
            return departmentName;
        }
    
        public void setDepartmentName(String departmentName) {
            this.departmentName = departmentName;
        }
    }
    
    
  • 在Employee中增加Department属性

    public class Employee {
    
        private Integer id;
        private String lastName;
        private String email;
        private String gender;
        public Department department;
    }
    
  • 数据库中Employee增加Department id

  • 查询与员工对应的部门和id

    1. 级联查询:级联属性封装结果集 ,利用resultMap将查询出来的deplement封装进属性
        <!--查询员工对应的部门,通过sql直接查询出对应的部门的名称与id -->
    <!--public Employee geeEmployeeAndDept(Integer id); -->
    <select id="geeEmployeeAndDept" resultMap="myEmpPlus">
      select e.id id , e.last_name last_name , e.gender gender ,e.d_id d_id ,d.id did
      ,d.dept_name dept_name from tbl_employee e,tbl_department d
      where e.d_id = d.id and e.id = #{id}
    </select>
    
    <!--级联查询:级联属性封装结果集 -->
    <resultMap id="myEmpPlus" type="com.wjl.mybatis.bean.Employee">
        <id column="id" property="id"></id>
        <result column="last_name" property="lastName"></result>
        <result column="email" property="email"></result>
        <result column="gender" property="gender"></result>
        <result column="did" property="department.id"></result>
        <result column="dept_name" property="department.departmentName"></result>
    </resultMap>
    
    1. 使用association定义关联单个对象的封装规则,将查询出的depeid与名字封装成department对象
     <!--使用association定义关联单个对象的封装规则 -->
    <resultMap id="myEmpPlus2" type="com.wjl.mybatis.bean.Employee">
        <id column="id" property="id"></id>
        <result column="last_name" property="lastName"></result>
        <result column="email" property="email"></result>
        <result column="gender" property="gender"></result>
        <!-- association 用来指定可以联合的javaBean对象
        property="department":指定哪个属性可以是联合的对象
        javaType="" 指定这个属性对象的类型
        -->
        <association property="department" javaType="com.wjl.mybatis.bean.Department">
            <id column="did" property="id"></id>
            <result column="dept_name" property="departmentName"></result>
        </association>
    </resultMap>
    
    
    1. 使用association进行分步查询,先按照员工id查询出员工信息 根据查出ID员工信息中d_id 值去部门表查出部门信息,再将部门设置到员工中
      <resultMap id="myEmpPlus3" type="com.wjl.mybatis.bean.Employee">
        <id column="id" property="id"></id>
        <result column="last_name" property="lastName"></result>
        <result column="email" property="email"></result>
        <result column="gender" property="gender"></result>
        <!-- association 定义关联对象的封装规则
            select: 表明当前属性是调用select方法查询出的结果
            column : 指定将哪一列的值传给这个方法
    
            流程:使用select指定的方法(闯入cloumn指定的这列参数的值)查出对象,封装给对象
        -->
        <association property="department" select="com.wjl.mybatis.dao.DepartmentMapper.getDeptById"
            column="d_id">
        </association>
    </resultMap>
    
    
       <!--  首先查询员工的详细信息-->
    <select id="getEmpByIdStep" resultMap="myEmpPlus4">
        select * from tbl_employee where id = #{id}
    </select>
    
    <!-- 根据查询出来的depid查询部门信息-->
     <select id="getDeptById" resultType="com.wjl.mybatis.bean.Department">
        select id,dept_name departmentName  from tbl_department where id = #{id}
    </select>
    

    这样分段查询时会将sql一起执行,全部查询出来,但有时候我们不需要查询出部门信息,只有需要
    的时候再进行查询,可以设置懒加载进行查询,全局懒加载开启后,可以在查询的 association 标签上设置fetchType=“lazy” (collection开启懒加载也是如此):表示使用延迟加载(需要全局开启懒加载)

     <settings>
            <!-- 	全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。 -->
            <setting name="lazyLoadingEnabled" value="true"></setting>
            <!--当设置为‘true’的时候,懒加载的对象可能被任何懒属性全部加载。否则,每个属性都按需加载. -->
            <setting name="aggressiveLazyLoading" value="false"></setting>
        </settings>
    
  • 情景二:查询部门时将所该部门下的员工查询出来

    1. collection嵌套结果集的方式,定义关联的集合类型元素的封装规则 ,在查询部门的时候将员工的信息查询出来,在使用resultMap中的collection进行封装结果集
     <select id="getDeptByIdPlus" resultMap="mydept">
       select d.id did, d.dept_name dept_name,e.id eid,e.last_name last_name ,e.email email ,e.gender gender
       from tbl_department d left join tbl_employee e on d.id = e.d_id where d.id = #{id}
    </select>
    
     <resultMap id="mydept" type="com.wjl.mybatis.bean.Department">
            <id column="did" property="id"></id>
            <result column="dept_name" property="departmentName"></result>
            <!--定义关联的集合类型的属性封装规则
                ofType:指定集合中的元素的类型
            -->
            <collection property="emps" ofType="com.wjl.mybatis.bean.Employee">
                <!--定义集合中元素的封装规则 -->
                <id column="eid" property="id"></id>
                <result column="last_name" property="lastName"></result>
                <result column="email" property="email"></result>
                <result column="gender" property="gender"></result>
            </collection>
        </resultMap>
    
    1. 分步查询,如上述的分步查询,首先查询出部门的id,再根据部门id去查找员工
        <resultMap id="mydeptstep" type="com.wjl.mybatis.bean.Department">
            <id column="id" property="id"></id>
            <result column="dept_name" property="departmentName"></result>
                <collection property="emps"
                            select="com.wjl.mybatis.dao.EmployeeMapperPlus.getEmpsByDepid"
                            column="id" fetchType="lazy"
                >
                </collection>
    
        </resultMap>
    

    扩展:将多列的值传递过去
    将多列的值封装map传递,column="{key=column1,key2=column2}"
    fetchType=“lazy” :表示使用延迟加载(需要全局开启懒加载 -lazy:延迟加载 -eager :立即

删除操作

  • 使用delete标签进行删除操作
<delete id="deleteEmpById">
        delete from tbl_employee where id = #{id}
    </delete>
上一篇:springboot-数据验证@Validated


下一篇:springboot笔记三