深入浅出Mybatis——实战学习

Mybatis框架概述

前端框架

  1. 前端开发的框架
    Angular.js,React.js,Vue.js

  2. 前端UI框架
    方便后端人员实现精美的前端页面搭建——Extjs,jquery ui,easy ui,bootstrap,layui

  3. 后端框架
    表现层框架(Controller):servlet
    struts,xwork,struts2,springmvc

在SpringMVC编程中一般分为四层:

1.表示层:(jsp、html 主要就是界面的展示)
2.控制层:(Contoller、Action)控制界面跳转
3.业务层:(Service)调用DAO层,实现解耦合目的,虽然不要它也可以运行项目,但是会使项目后期的延展和维护变得困难
4.持久层:(DAO)也叫数据访问层,实现对数据库的访问

持久层框架(Dao)JDBC:Hibernate(hql),ibtais(xml),Mybatis
整合框架:EJB,spring

SSH:struts/struts2 spring hibernate

SSM:springmvc spring mybatis

Mybatis是一个优秀的持久层框架,它对jdbc几乎所有的数据库操作进行了封装(包括加载驱动、创建connection、创建statement、手动设置参数、结果集检索等繁琐操作),使开发者只需要关注SQL本身。

Mybatis框架搭建

准备工作

创建学生表,创建基础数据

创建学生表:
Navicat创建数据库学生表
在学生表中添加基础信息
对应数据库表构造Student类:

package com.sinosoft.domain;

public class Student {

    private String id;
    private String name;
    private Integer age; //Integer可以表示空值

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

框架搭建

准备工作完成后开始进行框架搭建:

  1. 创建项目,搭建包结构
  2. 导入mybatis相关jar包和MySQL驱动包
  3. 导入log4j相关jar包
  4. 在src根下创建mybatis主配置文件mybatis-config.xml,搭建配置文件结构——dbcp,c3p0,druid alibaba 文档第三页 如下1-1
  5. 创建mapper包结构,创建SQL映射文件XxxMapper.xml 文档第四页 如下1-2
  6. 在src根下引入log4j属性文件
  7. 搭建测试程序,测试根据ID查单条——关键语句 模板在文档第二页 如下1-3

1-1:

<?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>
    <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/test"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
<!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
    </mappers>
</configuration>

1-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">
<mapper namespace="org.mybatis.example.BlogMapper">
 <select id="selectBlog" resultType="Blog">
 select * from Blog where id = #{id}
 </select>
</mapper>

1-3:

	String resource = "org/mybatis/example/mybatis-config.xml";
	InputStream inputStream = Resources.getResourceAsStream(resource);
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

数据库运行测试

框架搭建好后,进行数据库运行测试:

package com.sinosoft.test;

import com.sinosoft.domain.Student;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class Test1 {

    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        //输入流
        InputStream inputStream = null;
        try {
            //通过加载Mybatis的主配置文件mybatis-config.xml,创建输入流对象
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        /*
        SqlSessionFactoryBuilder:SqlSessionFactory的建造者,
        通过该建造者对象调用建造方法,为我们创建一个SqlSessionFactory对象
        sqlSessionFactory对象唯一的作用就是为我们创建SqlSession对象
         */
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //我们未来所有的操作,使用的都是SqlSeeion对象
        //例如增删改查,处理事务等等,都是统一使用session对象来完成
        SqlSession session = sqlSessionFactory.openSession();

        /*
        需求:根据id查单条
        如果取得的是单挑记录,我们调用的是selectOne方法
        参数1:根据命名空间,sqId的形式找到我们需要使用的sql语句
        参数2:我们要为sql语句中传递的参数
         */
        Student s = session.selectOne("test1.getById","A0002");
        System.out.println(s);
        session.close();
            
        
        //查询学生信息表中所有的记录
        List<Student> sList = session.selectList("test1.getAll");
        for(Student s:sList){
            System.out.println(s);
        }
        session.close();

        //数据库添加操作
        Student s = new Student();
        s.setId("A0005");
        s.setName("cxk");
        s.setAge(66);
        int count = session.insert("test1.add",s);
        System.out.println("================="+count);
        session.commit(); //mybatis是手动提交事务
        session.close();

        //数据库修改操作
        Student s = new Student();
        s.setId("A0001");
        s.setName("sjkskks");
        s.setAge(111);
        session.update("test1.update",s);
        session.commit();
        session.close();

        //数据库删除操作
        session.delete("test1.delete","A0004");
        session.commit();
        session.close();

    }
}

对应的mapper.xml映射中的sql语句代码为:

<?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="test1">
    <!--
        namespace:命名空间
        不同的mapper映射文件使用namespace来做区分
        不同的mapper映射文件所使用的namespace的命名不允许出现重复

        sql语句必须要写在相应的标签当中
        <insert>:在标签对中写insert开头的sql语句,处理添加操作
        <update>:在标签对中写update开头的sql语句,处理修改操作
        <delete>:在标签对中写delete开头的sql语句,处理删除操作
        <select>:在标签对中写select开头的sql语句,处理查询操作
        parameterType:为sql语句传递的参数
        resultType:为sql语句操作后返回的结果类型

        注意在未来实际项目开发中,所有的标签必须要写id属性
        <selsct>标签parameterType可以省略不写
                       resultType属性必须得写
        对于<insert><update><delete>这3个标签
        通常我们只写id属性,其他属性一概不写
        -->
    <select id="getById" parameterType="java.lang.String" resultType="com.sinosoft.domain.Student">
        select * from student where id=#{id}
    </select>

    <!--
        如果返回的是多条记录,那么resultType返回值类型,应该写为集合的泛型
    -->
    <select id="getAll" resultType="com.sinosoft.domain.Student">
        select * from student
    </select>

    <insert id="add" parameterType="com.sinosoft.domain.Student">
        insert into student(id,name,age) values (#{id},#{name},#{age})
    </insert>

    <update id="update" parameterType="com.sinosoft.domain.Student">
        update student set name=#{name},age=#{age} where id=#{id}
    </update>

    <delete id="delete">
        delete from student where id=#{id}
    </delete>

</mapper>

MyBatis结合dao层的开发

结合原始dao的开发

MyBatis对dao层动态代理的支持

我们以前用过动态代理,我们是在业务层使用的,在业务层使用动态代理是为了实现事务管理,业务层的动态代理是我们自己手写的,业务层之所以使用动态代理,是因为业务层本身就是用来处理业务逻辑的,事务相关的代码,不方便放在业务层处理,所以我们想到使用代理类帮业务层去处理。

现在我们要在dao层也要加入动态代理。dao层之所以创建代理类,是因为写dao层实现类本身就是一种不方便,在结合了MyBatis动态代理机制后,以后的实际项目开发,dao层的impl就不写了。Mybatis的动态代理不用我们自己手写,在Mybatis中已经集成好的一种机制,我们直接拿来使用就可以。

开发规则:

  1. 在Mapper.xml中将namespace设置为UserDao.java接口的权限定名
	<mapper namespace="sinosoft.dao.StudentDao">

配置mapper映射的namespace接口名称

  1. 将Mapper.xml中statement的id和UserDao.java接口的方法名保持一致
	<select id="getById" parameterType="java.lang.String" resultType="com.sinosoft.domain.Student">
        select * from student where id=#{id}
    </select>

    <insert id="save">
        insert into student(id,name,age) values(#{id},#{name},#{age})
    </insert>

配置mapper中的执行sql语句的id名称与接口中的方法名称

  1. 将Mapper.xml中statement的parameterType和UserDao.java接口的方法输入参数类型保持一致
    传入参数类型
    接口中方法输入参数类型

  2. 将Mapper.xml中statement的resultType和UserDao.java接口的方法输出结果类型保持一致
    输出结果类型
    接口方法的返回结果类型
    返回学生类

  3. Dao业务层动态代理提交事务。
    将事务处理直接写进ServiceIMPL中,而获取成员属性的时候直接从Dao层中的UserDao的接口中获取并转换成session对象。之后将Dao层中的DaoIMPL删除,将mapper.xml配置文件放入Dao层中,更改mapper.xml中的namespace路径映射到UserDao.class中,最后再将mybatis-config.xml中的mapper源路径更新一下

需要变动的地方
更改获取的对象
更改mapper.xml中的namespace
更改mybatis-config.xml中的mapper resourse

MyBatis全局配置文件

  1. properties:

    加载数据库连接属性文件

配置文件
数据库驱动配置文件

  1. settings全局配置参数

    Mybatis框架运行设置一些全局配置参数,比如:开启二级缓存,开启延迟载等等
    详细参考:mybatis-settings.docx
    注意:设置全局参数会影响mybatis框架运行,要谨慎设置

<!--
        设置与数据库交互的环境,可以在此处配置二级缓存,配置查询延迟加载策略等等
        配置的目的是为了更加有效的查询表中的记录

        在实际项目开发中,settings的设置基本没用
        因为settings对于查询的优化,得到的效果不明显

        对于海量级别的数据,使用settings配置优化,起不到任何的效果
        对于数据量较少的项目,对于查询的效率要求的比较低,也没有必要使用settings配置

        如果遇到了海量级别的数据,我们该如何去提高查询的效率呢?
        基础操作:
            对于常用的查询条件的字段,设置索引(相当于在书里加目录)
        高级操作:
            使用nosql数据库,redis(缓存数据库)
        专业操作:
            搜索引擎solar和Elasticsearch
            针对于电商行业


    -->
    <settings>
        <setting name="" value=""/>
    </settings>
  1. typeAliases

    设置类型别名,框架默认支持的别名,参见Mybatis默认支持的别名.docx

    表示为mapper映射文件中的domain起别名

<typeAliases>
        <!--
        方式1:
            为指定的类分别起别名,别名的命名由我们自己来决定

            type:要为哪个domain起别名, 填写包,类名称
            alias:别名的名字

        -->
<!--   <typeAlias type="com.sinosoft.domain.Student" alias="stu"/>-->

        <!--
            方式2:
                使用package标签批量起别名
                别名是Mybatis默认为我们取好的,命名不是由我们自己决定,别名为类名(类名的字母不区分大小写)
                虽然字母不区分大小写,但是还是要按照约定俗成的规则来进行填写
                name:指定一个包结构,表示在该包下,所有的domain自动起好了别名
        -->

        <!--
            总结:
                (1)如果未来实际项目开发中,如果公司需要使用起别名的机制,我们要用批量起别名的方式
                (2)在市场上也有很多企业崛起使用Mybatis起别名的机制
                    公司会认为将domain写成全路径,可以有效的提供代码的可读性
        -->

        <package name="com.sinosoft.domain"/>

    </typeAliases>

指定mapper映射文件路径:

<mappers>

        <!--
            方式1:使用resource属性,指定mapper映射文件
        <mapper resource="com/sinosoft/dao/StudentDao.xml"/>

        -->

        <!--
            方式2:
                使用class属性,找到dao层接口的全路径
                <mapper class="com.sinosoft.dao.StudentDao"/>
        -->

        <!--
            方式3:
                批量注册,name属性,指向dao层的包,表示在该dao包下,所有的mapper映射文件自动注册
        -->

        <!--
            总结:未来实际项目开发中,我们一定是批量注册mapper映射文件
        -->
        <package name="com.sinosoft.dao"/>

    </mappers>

Mybatis映射文件

  1. 设置参数类型:

    parameterType用于设置输入参数的Java类型,parameterType的值为参数类型的Java类型或者别名,Sql语句获取参数的值使用#{}或者${},使用时可省略

    (1)使用简单数据类型(8基本数据类型+String)为参数
    (2)使用引用数据类型为参数
    (3)使用domain对象类为参数
    (4)使用map为参数

package com.sinosoft.test;

import com.sinosoft.dao.StudentDao;
import com.sinosoft.util.SqlSessionUtil;

public class Test2 {
    public static void main(String[] args) {

        StudentDao studentDao = SqlSessionUtil.getSession().getMapper(StudentDao.class);

//        //1.测试:parameterType,使用简单数据类型 String
//        Student s = studentDao.select1("A0001");
//        System.out.println(s);

        //2.测试,parameterType使用简单数据类型int
        //查询出所有年龄为23岁的学员的详细信息
//        List<Student> sList = studentDao.select2(11);  //返回List集合,因为年龄11的不仅仅只有一个
//        for (Student s:sList){
//            System.out.println(s);
//        }

        //3.测试parameterType
        //需求:查询出姓名为wsc,年龄为18岁的学员信息
        /*
            绝对不可以同时为sql语句传递多个参数
         */
//        List<Student> studentList = studentDao.select3("wsc",18);
//        for (Student s:studentList){
//            System.out.println(s);
//        }

        //如果我们要为sql语句传递多个参数,我们应该将多个参数封装到一个domain对象中,或者是打包到一个map集合中

        //4.测试parameterType,使用domain为参数Student s
        //需求:查询出姓名为wsc,年龄为18岁的学员信息
//        Student s = new Student();
//        s.setName("wsc");
//        s.setAge(18);
//        List<Student> sList = studentDao.select4(s);
//        for (Student ss:sList){
//            System.out.println(ss);
//        }

        //5.测试parameterType,使用map为参数
        //需求:查询出姓名为wsc,年龄为18岁的学员信息
//        Map<String,Object> map = new HashMap<String,Object>();
//        map.put("name","wsc");
//        map.put("age",18);
//        List<Student> sList = studentDao.select5(map);
//        for (Student ss:sList){
//            System.out.println(ss);
//        }
        
        /*总结:
            在实际项目开发中,使用domain引用类型,或者是使用map集合类型都可以为sql语句同时传递多个参数
            一般情况下,我们使用的domain就可以了
            当domain不符合需求的情况下,我们一定要考虑使用map来传值
            例子:需求:请查询出姓名为wsc,班级为一年一班的学员的详细信息
            select 
               from student
               join classroom c
               on s.classroomId=c.id
               where s.name=#{wsc} and c.name={一年一班}
            
            
            在实际项目开发中,一定要学会使用为sql传值的这几种方式
            但是对于在<select>中的parameterType属性,一般我们都是省略不写的
            */
    }
}

对应的mapper.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="com.sinosoft.dao.StudentDao">

    <!--<select id="getAll" resultType="Student">
        select * from student;
    </select>

    <select id="getById" parameterType="java.lang.String" resultType="com.sinosoft.domain.Student">
        select * from student where id=#{id}
    </select>

    <insert id="save">
        insert into student(id,name,age) values(#{id},#{name},#{age})
    </insert>-->

    <!--
        对于parameterType:
        java.lang.String好使
        String好使
        string好使
        strinG好使
        STRING好使
        str不好使
        省略不写好使
    -->
    <!--
        使用简单类型(8大基本数据类型+String)为参数
        在#{}中的标识符可以随意去写
        但是虽然可以随意写,还是要写的有意义
    -->

    <select id="select1" parameterType="string" resultType="Student">
        select * from student where id=#{id};
    </select>

    <select id="select2" parameterType="int" resultType="Student">
        select * from student where age=#{age};
    </select>

    <select id="select3" resultType="Student">
        select * from student where name=#{name} and age=#{age};
    </select>

    <!--
        如果我们为sql语句传递的参数类型为一个domain引用类型,那么#{}中的标识符必须是domain类的属性名
    -->
    <select id="select4" parameterType="Student" resultType="Student">
        select * from student where name=#{name} and age=#{age};
    </select>

    <!--
        如果我们为sql语句传递的参数类型为一个map类型
        那么#{}中的标识符必须是map的key
    -->
    <select id="select5" parameterType="map" resultType="Student">
        select * from student where name=#{name} and age=#{age};
    </select>


</mapper>

  1. #{}与${}
    使用在sql语句中的符号
    (1)#{}:表示占位符,可以有效防止sql注入,使用#{}设置参数无需考虑参数的类型

    (2) : 表 示 拼 接 符 , 无 法 防 止 s q l 注 入 , 使 用 {}:表示拼接符,无法防止sql注入,使用 sql使{}设置参数必须考虑参数的类型

${}拼接的使用

对应的mapper.xml映射文件
mapper.xml的映射文件

(3)传递简单类型参数:如果获取简单类型参数,#{}中可以使用value或其他名称。如果获取简单类型参数, 中 只 能 使 用 v a l u e 。 注 意 引 号 的 引 入 s e l e c t ∗ f r o m s t u d e n t w h e r e i d = ‘ {}中只能使用value。注意引号的引入 select * from student where id=‘ 使valueselectfromstudentwhereid={value}’

(4) 在没有特殊要求的情况下,通常使用#{}占位符

(5)有些情况必须使用 , 比 如 : 需 要 动 态 拼 接 表 名 , s e l e c t ∗ f r o m {},比如:需要动态拼接表名,select*from selectfrom{tablename},比如:动态拼接排序字段:select*from tablename order by ${username} desc

(6)重点案例:
使用${}执行like模糊查询
使用#{}执行like模糊查询

//7.测试:Like模糊查询 方式1:使用${}了解即可
        /*List<Student> sList = studentDao.select7("s");
        for (Student ss:sList){
            System.out.println(ss);
        }*/

        //8.测试:Like模糊查询 方式1:使用#{} 了解
        /*List<Student> sList = studentDao.select8("%s%");
        for(Student ss:sList){
            System.out.println(ss);
        }*/

        //9.测试:Like模糊查询 方式1:使用#{} 掌握
        /*List<Student> sList =  studentDao.select9("s");
        for (Student ss:sList){
            System.out.println(ss);
        }*/

对应的mapper.xml映射

	<select id="select7" resultType="Student">
        select * from student where name like '%${value}%';
    </select>

    <select id="select8" resultType="Student">
        select * from student where name like #{"%s%"};
    </select>


    <!--
        '%'空格#{}空格'%'
        以上空格不能省略
    -->
    <select id="select9" resultType="Student">
        select * from student where name like '%' #{name} '%';
    </select>
  1. resultType
    设置返回值类型:
    (1)返回简单类型
//10.测试:resultType返回String类型
        //需求:查询出编号为A0001的学员的姓名
        /*String name = studentDao.select10("A0001");
        System.out.println(name);*/

        //11.测试:resultType返回String类型集合
        //需求:查询出所有学生的姓名
        /*List<String> sList = studentDao.select11();
        for (String name:sList){
            System.out.println(name);
        }*/

        //12.测试:resultType返回int类型
        //需求:查询出表中一共有多少条信息
        /*int count = studentDao.select12();
        System.out.println(count);*/

对应的mapper.xml映射

	<select id="select10" resultType="String">
        select name from student where id=#{id};
    </select>

    <select id="select11" resultType="String">
        select name from student;
    </select>

    <select id="select12" resultType="int">
        select count(*) from student;
    </select>

(2)返回pojo(domain Student)

//13.测试:resultType 返回domain引用类型Student
//        Student student = studentDao.select6("A0001");
//        System.out.println(student);

对应的mapper.xml映射文件

	<select id="select6" resultType="Student">
        select * from student where id='${value}';
    </select>

(3)返回hashmap(注意返回值类型)

 //14.测试:resultType 返回map类型
        /*
        * <select id="" resultType="Student">
           select * from student;
          </select>
          * 当执行了sql语句后,通过查询得到的结果id,name,age
          * 根据返回值的类型,会自动为我们创建出来一个该类型的对象,由该对象将查询的结果封装起来
          *
          * Student s1 = new Student();
          * s.setId("A0001");
          * s.setName("sjkskks");
          * s.setAge(23);
          *
          * 当查询出来了第二条记录,根据返回值类型,再一次创建出来一个对象,封装第二条记录的值
          * Student s2 = new Student();
          * s.setId("A0002");
          * s.setName("lh");
          * s.setAge(11);
          *
          * Student s6 = new Student();
          * s.setId("A0006");
          * s.setName("SA");
          * s.setAge(2);
          *
          * 多条记录封装成为了多个Student对象
          * Mybatis系统会自动创建出来一个List集合来保存这些对象
          * List<Student> sList = new ArrayList();
          * sList.add(s1);
          * sList.add(s2);
          * ...
          * sList.add(s6);
          *
          *
          * --------------------------------------------------
          *
          * <select id="" resultType="Student">
            select * from student;
            </select>
            *
            * 当执行了sql语句之后,通过查询得到的结果id,name,age
            * 根据返回值类型,map会自动为我们创建出来一个该类型的对象,由该对象将查询的结果封装起来
            * Map<String,Object> map1 = new HashMap<>();
            * map1.put("id","A0001");
            * map1.put("name","sjkskks");
            * map1.put("age","23");
            *
            * 当查询出来了第二条记录,根据返回值类型,再一次创建出来一个对象,封装第二条记录的值
            *
            * 当执行了sql语句之后,通过查询得到的结果id,name,age
            * 根据返回值类型,map会自动为我们创建出来一个该类型的对象,由该对象将查询的结果封装起来
            * Map<String,Object> map2 = new HashMap<>();
            * map1.put("id","A0002");
            * map1.put("name","lh");
            * map1.put("age","11");
            * ...
            * ...
            * map6.put()...;
            *
            *
            *多条记录封装成为了多个map对象
            *Mybatis系统会自动创建出来一个map集合来保存这些对象
            * List<Map<String,Object>> mapList = new ArrayList<>();
            * mapList.add(map1);
            * mapList.add(map2);
            * ...
            * mapList.add(map6);
            *
            * 对于sql语句查询的结果,我们使用domain来封装这些结果,很方便,为何还使用map呢??
            * 因为对于查询的结果有很多的情况,使用domain来封装不了,所以我们使用map来保存结果
            *
            * 例如:
            *   需求:根据姓名来分组,查询出来每一个姓名对应的数量
            *   叫sjkskks的多少人,叫lh的多少人
            *   ...
            *
            * select name,count(*)
            * from student
            * group by name
            *
            * 对于以上查询结果,使用domain能封装结果值吗?
            * 不能,因为domain有name属性,但是没有count属性
            *
            * 所以返回map一定可以保存查询得到的结果
            *
        * */
        /*List<Map<String, Object>> mapList = studentDao.select14();
        for (Map<String, Object> map : mapList) {
            Set<String> set = map.keySet(); //将键值拆开
            for (String key : set) {
                System.out.println("key:" + key);
                System.out.println("value:" + map.get(key));   //通过拆开的键值来遍历查找map中的value值
            }
            System.out.println("---------------------------------------");
        }*/

对应的mapper.xml映射

	<select id="select14" resultType="map">
        select * from student;
    </select>

(4)当查询字段名和pojo属性名不一致时的解决方案
a.为字段起别名,别名为类中属性名

//15.测试,resultType 当数据库表字段名称和domain类属性名称不一致时的处理,方式一:起别名
        /*List<Student> studentList = studentDao.select15();
        for (Student name:studentList){
            System.out.println(name);
        }*/

对应的mapper.xml映射

<!--
    resultMap 是用来做数据库表字段和类属性名一一对应关系所用的标签
    id:resultMap标签对的唯一标识,将来在使用该resultMap标签的时候,使用id来找到这组标签
    type:指定一个类型,要与数据库表一一对应,建立起表字段和类属性的名字一一匹配的关系
    -->
    <select id="select15" resultType="Student">
        select

         id,
         fullname as name,
         age

         from student;
    </select>

b.使用resultMap在mapper.xml映射文件中将表字段与domain类属性名称建立起一一匹配的关系

//16.测试,resultType 当数据库表字段名称和domain类属性名称不一致时的处理,方式二:使用resultMap
        /*List<Student> studentList = studentDao.select16();
        for (Student name:studentList){
            System.out.println(name);
        }*/

对应的mapper.xml映射

<!--
    resultMap 是用来做数据库表字段和类属性名一一对应关系所用的标签
    id:resultMap标签对的唯一标识,将来在使用该resultMap标签的时候,使用id来找到这组标签
    type:指定一个类型,要与数据库表一一对应,建立起表字段和类属性的名字一一匹配的关系
    -->
    <resultMap id="stuMap" type="Student">

        <!--
            id标签:用来配置主键的对应关系
            result标签:用来配置普通字段对应关系的

            对于student表,表结构是一个id,两个普通字段
            所以需要一个id标签,两个result标签

            property属性:配置的是类中的属性名
            column属性:配置的是表中的字段名
            这样就能够建立起类属性的表字段一一对应的关系
        -->
        <id property="id" column="id"/>
        <result property="name" column="fullname"/>
        <result property="age" column="age"/>

    </resultMap>

    <select id="select16" resultMap="stuMap">

        select * from student;

    </select>

Mybatis动态SQL机制

使用动态sql,动态拼接sql语句
情况1:如果查询数据什么都不填
String sql = “select * from tbl”;
情况2:只填写了姓名
String sql = “select * from tbl where name like ?”;
情况3:只填写了姓名与性别
String sql = “select * from tbl where name like ? and gender=?”;
情况4:只填写了姓名与性别,年龄
String sql = “select * from tbl where name like ? and gender=? and age=?”;
等等等等。。。。多种查询匹配组合

在实际项目开发中,我们对于这种需求,肯定是要将sql语句写成动态的形式,动态sql语句的核心思想是,有哪个查询条件,就动态的在where关键字后面挂载哪个查询条件

String sql = “select * from tbl”;
if(name != null) {
sql += “where name like ?”;
}
if(gender != null) {
sql += “and gender=?”;
}

where if 标签的使用:
实现代码如下:

//17.测试,动态sql where标签+if标签
        Student student = new Student();
        //student.setName("s");
        student.setAddress("撒");
        List<Student> studentList = studentDao.select17(student);
        for (Student s:studentList){
            System.out.println(s);
        }
    }

对应的mapper.xml映射:

<select id="select17" parameterType="Student" resultType="Student">
        select * from student
           <!--where标签:
                当where标签在使用的时候,必须要搭配where标签对中的if标签来使用
                当通过if标签的判断,如果有查询条件,则展现where关键字,如果没有查询条件则不展现where关键字
               -->
        <where>
            <if test="name!=null and name!= ''">
                name like '%' #{name} '%'
            </if>
            
            <if test="address!= null and address!=''">
                and address like '%' #{address} '%'
            </if>
        </where>
    </select>

foreach标签的使用:
实现代码如下:

		//18.测试:动态sql foreach标签
        //查询编号为A0001,A0002,A0003的学员信息
        String strArr[] = {"A0001", "A0002", "A0003"}; //前端往后端传用数组,后端往前端传用集合
        List<Student> studentList = studentDao.select18(strArr);
        for (Student student : studentList) {
            System.out.println(student);
        }

对应的mapper.xml映射文件:

	<select id="select18" resultType="Student">
        select * from student
        where id in
        <!--
            foreach标签:用来遍历传递来的数组参数
            collection:标识传递参数的类型
                        array:数组
                        List:集合
            item:每一次遍历出来的元素,在使用该元素的时候,需要用在#{}中
            open:拼接循环开始符号
            close:拼接循环结束的符号
            separator:元素与元素之间的分割符
        -->

        <foreach collection="array" item="id" open="("  close=")" separator=",">
            #{id}
        </foreach>
    </select>

mybatis中的sql片段的使用:
实现代码如下:

		//19.测试:sql片段
        Student s = studentDao.select19("A0001");
        System.out.println(s);

对应的mapper.xml映射:

	<!--
        使用sql标签制作sql片段
        sql片段的作用是用来代替sql语句中的代码
        如果你的mapper映射文件中的sql语句某些代码出现了大量的重复,我们可以使用sql片段来代替他们
        id:sql片段的唯一标识,将来找到sql片段使用id来进行定位

        将来的实际项目开发中,使用sql片段用来代替重复率高,且复杂的子查询
        select * from student
        where name=(
             select xxxxx
             xxxxx
                   (
                        select .....
                   )
        )
        注意:
            对于sql片段
            在同一个mapper下,大量的出现重复的子查询的几率不高,所以一般情况下没有使用sql片段的必要
            在实际项目开发中,如果你大量的使用sql片段,会大大的降低sql语句的可读性
            在很多企业中,干脆摒弃使用sql片段的机制

    -->
    <sql id="sql1">
        select * from student
    </sql>
    <select id="select19" resultType="Student">

        <include refid="sql1"/> where id=#{id}

    </select>

Mybatis多表联查

关联学生表和班级表:

  1. 查询出学生姓名和班级名称
    新建班级表:
    新建班级表
    班级表的信息
    实现代码如下:
		//20.测试:多表联查  查询出学生姓名和班级名称
        //多表联查返回的数据有时候不能用domain来返回,所以最好用map
        List<Map<String,Object>> mapList = studentDao.select20(); 
        for (Map<String,Object> map:mapList){
            Set<String> set = map.keySet();   //key不可重名
            for (String key:set){
                System.out.println("key:"+key);
                System.out.println("value:"+map.get(key));
            }
            System.out.println("--------------------------------------------------");
        }

对应mapper.xml映射文件如下:

	<select id="select20" resultType="map">

        select

        s.name as sname,
        c.name as cname

        from student s
        join  classroom c
        on s.classroomId=c.id

    </select>
  1. 查询出学生和班级所有信息,加VO(Value Object)
    实现代码如下:
		//21.测试:多表联查 查询出学生和班级所有信息,加VO
        /*
        * 在实际项目开发中,如果需要为前端展现的数据,使用一个domain类型不足以表现出来这些数据
        * 这时,我们可以考虑使用两种技术来实现
        * 分别为:
        *   使用map以及使用vo
        *
        * 例如我们现在的需求
        *      查询出学生和班级所有信息
        *      得到的结果  使用学生的domain或者班级的domain都不能够封装这些结果
        *      所以我们可以使用map去保存这些信息
        *      同时我们也可以使用vo类来保存这些信息
        *
        *      vo指的是创建出来一个类,这个类中的属性是完全由我们自己去定义,属性会保存所有需要展现的信息,
        *      例如我们现在的这个例子,我们可以使用vo来封装所有与学生和班级相关的信息
        *
        *      vo
        *           student
        *           classroom
        * */
        List<StudentAndClassroomVo> voList = studentDao.select21();
        for (StudentAndClassroomVo vo:voList){
            System.out.println(vo);
        }

对应的mapper.xml映射文件为:

 	<select id="select21" resultType="com.sinosoft.vo.StudentAndClassroomVo">

        select

        s.id sid,
        s.name sname,
        s.age sage,
        s.address saddress,
        c.id cid,
        c.name cname

        from student s
        join classroom c
        on s.classroomId=c.id

    </select>
  1. 查询出带有字母Z的学生和班级所有信息
    实现代码如下:
		//22.测试:多表联查 查询出带有字母z的学生和班级所有信息
        List<StudentAndClassroomVo> voList = studentDao.select22("s");
        for (StudentAndClassroomVo vo:voList){
            System.out.println(vo);
        }

        /*
        * 实际项目开发中,如果要为前端同时提供多组值,那么我们应该使用map还是vo呢?
        * 如果前端的需求的重复率不高,那么我们选择临时使用map就可以了
        * 如果前端对于该需求的重复率较高,那么我们可以创建一个vo类来使用,非常方便
        * */

对应的mapper.xml映射文件:

	<select id="select22" resultType="com.sinosoft.vo.StudentAndClassroomVo">

        select

        s.id sid,
        s.name sname,
        s.age sage,
        s.address saddress,
        c.id cid,
        c.name cname

        from student s
        join classroom c
        on s.classroomId=c.id

        where s.name like '%' #{name} '%'
    </select>

Mybatis工具包(ServiceFactory等)

工具包
SqlSessionUtil的实现代码如下:

package com.sinosoft.util;

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 java.io.IOException;
import java.io.InputStream;

public class SqlSessionUtil {

    private SqlSessionUtil(){
        
    }

    private static  SqlSessionFactory sqlSessionFactory;

    static {
        String resource = "mybatis-config.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    private static ThreadLocal<SqlSession> t = new ThreadLocal<SqlSession>();  //因为在声明的时候已经确定好了泛型,创建对象的时候jvm会自动进行类型转换

    //取得sqlsession对象
    public static SqlSession getSession(){

        SqlSession session = t.get();
        if (session == null){

            session = sqlSessionFactory.openSession();
            t.set(session);

        }
        return session;
    }
    //关闭sqlsession对象
    public static void myClose(SqlSession session){

        if(session != null){
            session.close();
            t.remove(); //这句必须加,非常容易忘,不然会在ThreadLocal存在遗留
        }

    }

}

TransactionInvocationHandler的代码实现如下:

package com.sinosoft.util;

import org.apache.ibatis.session.SqlSession;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class TransactionInvocationHandler implements InvocationHandler {

    //target:zs
    private Object target;
    public TransactionInvocationHandler(Object target){
        this.target = target;
    }

    //代理类的业务方法
    //代表李四的表白方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        SqlSession session = null;
        Object obj = null;

        try{

            session = SqlSessionUtil.getSession();
            //处理业务逻辑
            //method.invoke,代表张三的表白方法
            obj = method.invoke(target,args);
            //处理业务逻辑完毕后,提交事务
            session.commit();
        }catch(Exception e){

            session.rollback();
            e.printStackTrace();

        }finally {

            SqlSessionUtil.myClose(session);

        }

        return obj;

    }

    //取得李四对象
    public Object getProxy(){

        //取得代理类对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);

    }
}

ServiceFactory的代码实现如下:

package com.sinosoft.util;

public class ServiceFactory {

    //传递张三对象,得到李四对象的过程
    public static Object getService(Object service){

        return new TransactionInvocationHandler(service).getProxy();
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值