MyBatis的BUG之旅

简介

首先,为啥淘汰hibernate框架?
因为hibernate框架干的事情太多了,SQL语句都帮办了,而且全自动全映射情况导致每次查询都是把对应类的整个字段都查出来。效率太低了,于是使用轻量级的MyBitas半自动代替,收回SQL语句的编写权。
具体表现:
每数据库表对应的SQL语句都用XML文件配置,建立SqlSession对象通过命名空间+sql语句的唯一标识符进行SQL语句调用和返回值处理。

运行步骤

 * 1.根据xml配置文件(全局配置文件)创建一个SessionFactory对象
 * 2,SQL映射文件:配置每一个SQL,以及SQL的封装规则等。
 * 3,将SQL映射文件导入到全局配置文件中
 * 4,写代码:
 *        1),根据全局配置文件得到SqlSessionFactory;
 *        2),使用SqlSession工厂,获取SqlSession对象进行增删改查
 *            一个SqlSession对象就代表一次会话,用完需要关闭。
 *        3),sqlSession使用 命名空间+sql语句标识符,来告诉MyBatis执行那个sql语句,sql语句都是封装在sql映射文件中的

Error building SqlSession

XML语法详细介绍

/**
     * 1.根据xml配置文件(全局配置文件)创建一个SessionFactory对象
     * 2,SQL映射文件:配置每一个SQL,以及SQL的封装规则等。
     * 3,将SQL映射文件导入到全局配置文件中
     * 4,写代码:
     *        1),根据全局配置文件得到SqlSessionFactory;
     *        2),使用SqlSession工厂,获取SqlSession对象进行增删改查
     *            一个SqlSession对象就代表一次会话,用完需要关闭。
     *        3),sqlSession使用 命名空间+sql语句标识符,来告诉MyBatis执行那个sql语句,sql语句都是封装在sql映射文件中的
     * @throws IOException
     */
public class HelloWord {
    public static void main(String args[]) throws IOException {
        //加载全局配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
//        if(inputStream==null) System.out.println("输入流为空");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//        配置文件中含有对应的SQL语句的定义和修饰,加载出来可以通过方法使用。
        try (SqlSession session = sqlSessionFactory.openSession()) {
//            第一个参数sql语句的唯一标识:命名空间+SQLid
//            第二个参数SQL语句中参数
            User user = session.selectOne("mybatis.example.BlogMapper.selectId", 41);
            System.out.println(user);
//            不需要关掉session,因为是在try里面定义的,try结束后session自动销毁
//            session.close();
        }
    }
}

错误地方


<?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="mybatis.example.BlogMapper">
<!--    namespace :命名空间-->
<!--    id:sql语句的唯一标识符-->
<!--    resultType:返回值类型-->
    <select id="selectId" resultType="Entity.User">
        select * from user where id = #{id}
    </select>
</mapper>

修改后的代码

<?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="mybatis.example.BlogMapper">
<!--    namespace :命名空间-->
<!--    id:sql语句的唯一标识符-->
<!--    resultType:返回值类型-->
    <select id="selectId" resultType="Entity.User">
        select * from user where id = #{id}
    </select>
</mapper>
错误原因: 因为一开始多了一行空格,所以当加载XML文件的时候出错=》XML声明应该在所有文档内容之前,也就是说第一行必须是XML文档声明。

报错信息:
Xml declaration should precede all document content
翻译:
Xml声明应该在所有文档内容之前

在这里插入图片描述

接口式编程

在这里插入图片描述
注意:
这里UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
可以尝试输出UserMapper.class,得到的是Dao.UserMapper类路径,于是这也是为什么xml映射文件要和接口路径还有名字一样的原因,因为需要通过接口路径和名字查询对应的xml映射文件。

但是以后spring整合mybatis,映射文件就可以单独存放了于是就可以改变名字了,因为到时候会根据xml文件的namespace进行对应接口的匹配映射。
整合前:根据接口类路径找xml映射文件。
整合后:根据xml映射文件namespace匹配对应接口类。

//    接口与配置文件进行映射来操作增删改查
    public static void test() throws IOException {
        SqlSessionFactory sqlSessionFactory=getSqlSessionFactory();
        SqlSession sqlSession=sqlSessionFactory.openSession();
        //获取实现类=>实现类哪里来的?这里的UserMapper.class得到的结果就是UserMapper的类路径,从而找到对应的mapper配置文件
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        User user=userMapper.getUserById(41);
        //其实是代理对象,会为接口自动创建一个代理对象去执行增删改查方法。
        System.out.println(userMapper.getClass());
        System.out.println(user);
        sqlSession.close();
    }

增删改查SQL语句(resultType版)

原理:
jdbc查询得到的数据会先根据resultType进行封装,然后检查对应的接口函数的返回值类型(例:List),然后再次进行封装。
接口

package Dao;

import Entity.User;
import org.apache.ibatis.annotations.MapKey;

import java.util.List;
import java.util.Map;
//返回类型会先以映射文件类型resultType或resultMap为准,然后再根据接口函数的返回值类型进行封装,所以接口函数的返回值需要和映射文件匹配。
public interface DaoMapper {
//    允许增删改直接定义以下类型返回值
//    boolean,long,integer
    public User getUserById(Integer id);
    public boolean updateUser(User user);
    public long addUser(User user);
    public void deleteUserById(Integer id);
    public User getUserByIN(Integer id,String name);
    public List<User> getUserByLikeName(String firstName);
    public Map<String ,Object> getUserByIdReturnMap(Integer id);
//    需要使用注解确定作为键的属性,对应的是实体类的属性,因为返回的是根据resultType封装好的user对象,然后再将user对象进行键值对分配。
    @MapKey("user_name")
    public Map<String,User> getUserByLikeNameReturnMap(String firstName);
}
<?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="Dao.DaoMapper">
    <!--    namespace :命名空间:指定为接口的全类名
            id:sql语句的唯一标识符:改为接口方法的id
            resultType:返回值类型

            public User getUserById(Integer id);
            -->
    <!--    public User getUserById(Integer id);-->
    <select id="getUserById" resultType="user">
--         这里的字段usrname起了个别名user_name来对应实体类中的变量。
        select id,username user_name,birthday,sex,address from user where id = #{id}
    </select>
<!--    public User getUserByIN(Integer id,String name);-->
    <select id="getUserByIN" resultType="user">
        select id,username user_name,birthday,sex,address from user where id=#{arg0} and username=#{arg1}
    </select>
<!--    public List<User> getUserByLikeName(String firstName);模糊查询-->
<!--    如果返回是个集合要写resultType集合的元素类型-->
    <select id="getUserByLikeName" resultType="user">
--                                                                                       通配符作为参数传入进来
        select id,username user_name,birthday,sex,address from user where username like #{firstName}
    </select>
<!--    public Map<String ,Object> getUserByIdReturnMap(Integer id);-->
    <select id="getUserByIdReturnMap" resultType="map">
--         返回一条记录的map,key就是列名,值就是对应的值
        select id,username user_name,birthday,sex,address from user where id = #{id}
    </select>
<!--    public Map<Integer,User> getUserByLikeNameReturnMap(String firstName);-->
    <select id="getUserByLikeNameReturnMap" resultType="user">
--         先是返回User类型然后会根据接口函数返回值类型和指定的键值进行封装
         select id,username user_name,birthday,sex,address from user where username like #{firstName}
    </select>
    <!--    public void updateUser(User user);将传入对象属性通过get方法进行映射提取-->
    <update id="updateUser" parameterType="user">
        update user
        set username=#{user_name},birthday=#{birthday},sex=#{sex},address=#{address}
        where id=#{id}
    </update>
    <!--    public void addUser(User user);添加用户没有返回值,所以只有传入参数(可以省略)-->
    <!--    public int id;-->
	<!--    public String user_name;-->
	<!--    public Date birthday;-->
	<!--    public char sex;-->
	<!--    public String address;-->
    <insert id="addUser" parameterType="user" useGeneratedKeys="true" keyProperty="id">
--         添加完之后得到对应主键的值,值会存在对应的参数属性里面。    
    insert into `user`(`username`,`birthday`,`sex`,`address`)values (#{user_name},#{birthday},#{sex},#{address})
    </insert>
    <!--    public void deleteUserById(Integer id);-->
    <delete id="deleteUserById">
        delete from user where id=#{id}
    </delete>

</mapper>

查询结果封装(resultMap)

主要是将查询结果值封装到多个不同对象(association可以有多个)的封装规则

package Dao;

import Entity.User;

public interface DaoMapperPlus {
    public User getUserById(Integer id);
    public User getUserAndDepart(Integer id);
    public User getUserAndDepartStep(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">
<mapper namespace="Dao.DaoMapperPlus">

<!--    resultMap相比较resultType功能更加强大,但是都是针对的数据库查询结果返回值类型进行的封装=》只是针对一个对象-->

<!--    定义查询字段的封装规则-->
    <resultMap id="daoMapperPlus" type="Entity.User">
<!--        column指定列名,property指定属性名-->
        <id column="id" property="id"></id>
        <result column="username" property="user_name"></result>
<!--            其他列可以不写,会自动指定封装,前提是列名和属性名一样-->
    </resultMap>
<!--    public User getUserById(Integer id);-->
    <select id="getUserById" resultMap="daoMapperPlus">
        select * from user where id=#{id}
    </select>
<!--    联合查询
        级联属性封装-->
    <resultMap id="daoMapperDif" type="Entity.User">
        <id column="id" property="id"></id>
        <result column="username" property="user_name"></result>
        <result column="did" property="department.id"></result>
        <result column="dept_name" property="department.dept_name"></result>
    </resultMap>
    <resultMap id="daoMapperDifA" type="Entity.User">
        <id column="id" property="id"></id>
        <result column="username" property="user_name"></result>
        <result column="address" property="address"></result>
        <result column="sex" property="sex"></result>
        <result column="birthday" property="birthday"></result>
<!--        association指定联合的对象的封装规则
            property:指定联合的对象
            注意:使用association标签的时候需要把所有映射关系补齐
-->
        <association property="department" javaType="Entity.Department">
            <result column="did" property="id"></result>
            <result column="dept_name" property="dept_name"></result>
        </association>
    </resultMap>
<!--    public User getUserAndDepart(Integer id);查询用户的同时查询部门-->
   <select id="getUserAndDepart" resultMap="daoMapperDifA">
       SELECT u.id id,u.username username,u.birthday birthday,u.sex sex,u.address address,d.id did,d.dept_name dept_name
       FROM `tab_dept` d,`user` u  where u.d_id=d.id and u.id=#{id}
   </select>
<!--    action分步查询;效率高于一起查-->
    <resultMap id="daoMapperStep" type="Entity.User">
        <id column="id" property="id"></id>
        <result column="username" property="user_name"></result>
        <result column="address" property="address"></result>
        <result column="sex" property="sex"></result>
        <result column="birthday" property="birthday"></result>
<!--        分步查询将级联对象分到其对应的Mapper查询中去
            select:对应Mapper映射文件的查询方法
            column:数据库传回的对应值作为参数传送,也可以通过{dept_id=d_id,***=**}这样的map形式传递多个值-->
        <association property="department" select="Dao.DeptMapper.getDeptById" column="d_id">

        </association>
    </resultMap>
<!--    public User getUserAndDepartStep(Integer id);-->
    <select id="getUserAndDepartStep" resultMap="daoMapperStep">
        select * from user where id=#{id}
    </select>
<!--    延迟查询,基于分段查询的基础上-->
</mapper>

总结

运行时爆红或者爆黄都是配置文件出错了,配置不出来才会报错,不是具体的逻辑错误。
运行时服务器都是运行的web.xml文件及其自身导入的xml文件,配置成功完成后运行。
在这里插入图片描述

已标记关键词 清除标记
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质? 你是否想成为一名资深开发人员,想开发别人做不了的高性能程序? 你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹?   那么C++就是你个人能力提升,职业之路进阶的不二之选。 【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。 2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。 3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。 【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署; 2.吊打一切关于C++的笔试面试题; 3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。 【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块 基础篇 本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。 进阶篇 本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。 提升篇: 本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。
©️2020 CSDN 皮肤主题: 设计师:QLUGCL 返回首页