框架技术 ---- Mybatis介绍

Mybatis

内容管理

    • 三层架构
    • JDBC的缺陷、
    • 框架 framework
    • Mybatis
    • mybatis主配置文件
    • Mybatis映射文件mapper
      • select标签
    • mybatis简单使用
      • 数据库表创建
      • 加入maven依赖【坐标】,mysql驱动坐标
      • 创建entity保存表中的数据
      • 创建Dao接口和impl
      • 创建mybatis使用的配置文件【sql mapper xml】
      • 创建mybatis的主配置文件
      • 创建测试类【DaoImpl使用JDBC,所以也使用Mybatis】
    • mybatis默认关闭自动提交事务
    • 开启日志
    • Mybatis主要类
      • Resources
      • SqlSessionFactoryBuilder
      • SqlSessionFactory
      • SqlSession
    • Mybatis的工具类
    • DaoImpl使用Mybatis
    • Mybatis的动态代理session.getMapper

Javaweb —emmm……框架就是开发人员**用的东西


其实以现有的技术,已经能够跑通一个网站了;但是呢,Maven这强大的管理 和框架这么便捷的操作,所以还是用框架写吧


Maven使得之前的导包的操作变得十分简单,从这个阶段开始,IDE工具使用IDEA,但是Eclipse也会操纵,没有什么大问题;对于mybatis,其实还有一个类似的技术是hibernate;后面会提一提;技术挺多的,慢慢来---- 但是底层都差不多

之前的文章中提到了MVC的结构,也就是控制层、视图层和业务逻辑层;控制层为servlet;视图层为response或者jsp;业务逻辑层就是service层;控制层是最重要的,负责接收请求,调用service层;并用view层反馈—web架构;

三层架构

三层架构包括3层: 界面层(User interface layer )、业务逻辑层(Business logic layer 【biz】)、数据访问层(data access layer);

  • 界面层 【表示层、视图层】:主要功能就是接收用户的数据,显示请求得处理结果,使用web页面和用户交互;手机App就是表示层得,用户再app操作,业务逻辑在服务器端处理【jsp,html,servlet】
  • 业务逻辑层 : 接收表示层传递过来的数据,检查数据,计算业务逻辑,调用数据访问层获取数据【service】
  • 数据访问层【数据持久层】 : 与数据库相关联,主要实现对数据的增删查改,将存储在数据库中的数据提交给业务层;同时将业务层的处理的数据保存到数据库 【Dao】

三层架构就是一种垂直结构;和MVC的分块方式不同

三层的处理请求的交互 : 用户—> 界面层—>业务逻辑层—> 数据访问层---->DB数据库

三层对应的框架:
界面层 ---- > servlet ----> springMVC框架

业务逻辑层---->service----->spring框架管理

数据访问层----->dao ------> mybatis框架

JDBC的缺陷、

可以先来随便看一段代码

	public int  editModifyStudent(String stuno,String stuname,String stuclass) {
		Connection conn = null;
		PreparedStatement state = null;
		int count = 0;
		try {
			conn = DBUtil.getConnection();
			conn.setAutoCommit(false);
			String sql = "UPDATE student SET stuname=?,stuclass=? WHERE stuno = ?";
			state = conn.prepareStatement(sql);
			state.setString(1, stuname);
			state.setString(2, stuclass);
			state.setString(3, stuno);
			count = state.executeUpdate();
			//一定要记得提交
			conn.commit();
		} catch (SQLException e) {
			if(conn != null) {
				try {
					conn.rollback();
				} catch (SQLException e1) {
					e1.printStackTrace();
				}
			}
			e.printStackTrace();
		}finally {
			DBUtil.close(conn, state, null);
		}
		return count;
	}

这段代码还是使用工具类DBUtil将注册驱动和连接和释放资源的操作封装后的结果;可以看到,其实对于这个操纵;用户真正变得地方是

String sql = "UPDATE student SET stuname=?,stuclass=? WHERE stuno = ?";
state = conn.prepareStatement(sql);
state.setString(1, stuname);
state.setString(2, stuclass);
state.setString(3, stuno);

但是为了正常使用JDBC,却不得不写其他的操作;所谓JDBC编程6步,其实真正改变的就是sql语句而已,按照配置文件的方式,数据库的操作在文件中改即可;【代码量很长;但是代码没有任何亮点,所以需要封装起来】

总结来说就是

  • 代码量多,效率低
  • 关联conn、state、result的创建和销毁
  • 对于Result查询的结果,需要自己封装为List【交给上一层】
  • 重复的代码很多
  • 业务代码和数据库的操纵混在一起【比如查询学生,但是却要写很多数据库的代码,就是创建连接……这些不是业务中的】

封装成工具类DBUtil并没有很好解决上面的问题,所以需要更加彻底的封装

框架 framework

所谓的框架就是一个模板,这个模板 规定了一些条款和内容需要遵循【其实就是写好的东西,我们作为用户来使用,用别人的东西当然就不是随心所欲了】,然后非常简单的加入内容即可;框架中定义了一些功能,这些功能是可以使用的,可以在项目中加入自己的功能,之后这些功能就可以利用框架的功能来实现想要实现的强大的功能

framework是整个或部分系统的可复用的设计,表现为一组抽象的构件和构件之间的交互的方法;框架是开发者指定的骨架和模板;框架就是安全的软件 【半成品的软件,加入自己的功能即可,基础的功能不变】

框架的特点: 框架一般不是全能的,一般针对某一领域有效

Mybatis

Mybatis框架使用java编写提供了的持久层框架包括SQL Mapper和Data access Objects(Daos),解决的问题就是减轻JDBC的复杂性,不用重复创建Connection,Statement,不需要编写释放资源的代码,直接使用java对象,表示结果数据;mybatis是一个sql mapper框架,提供数据库的操作能力,其实就是一个增强的JDBC,使用mybatis,开发人员就只用写sql语句即可,专注业务处理

sql mapper : sql映射 : 可以将数据库表的一行数据看作一个java对象【一个表就是一个java entitiy类】

Mybatis可以封装的功能有:

  • 注册数据库的驱动 ; Class.forName(“com.mysql.cj.jdbc.Driver”);
  • 获取数据库连接和数据库操作对象和结果对象 Connection、Statement、ResultSet对象
  • 执行sql语句,不需要手动执行excute等方法
  • 从xml中获取sql,执行sql语句,把ResultSet结果自动转为java对象 — LIst集合

所以programmer只需要提供sql语句----- mybatis处理sql ---- 就可以得到List集合成java对象;

mybatis主配置文件

主配置文件放在maven项目的main/resources目录下,所以一个module只会对应一个数据库的连接,主配置文件发挥的作用就是数据库绑定;和之前写的配置文件db.properties相同,其中有各种的信息; 这个文件主要存放的是数据库的配置信息,和sql映射文件的位置

  1. 因为是xml文件,所以第一行还是xml文件的格式

  1. 之后就是对于xml文件的约束,约束之后就只能使用mybatis规定的标签【不约束就可以随便写,但是识别不了】
DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

mybatis-3-config.dtd 就是约束文件的名称;是dtd类型的

  1. 接下来就是configuration;这个是根标签,是整个文件的主体

  2. 在根标签configuration中有两个子标签,一个是environments;另外一个就是mappers

    • environments: 环境配置,数据库的连接信息,因为s;表明可以配置多个环境;一个environment对应的就是一个数据库的配置;其属性id是用来唯一标识整个environment的,可以自定义 default属性的值必须和某个environment的id相同,告诉使用哪个数据库的连接;访问哪个数据库

      • transactionManager : mybatis的事务的类型;其属性type有两个值 JDBC:表示使用JDBC中Connection的方法commit,rollback
      • dataSource: 表示数据源-- 连接数据库的;type属性表示数据源的类型,一般用POOLED表示使用连接池;下面就是property标签, 每一个property标签指定一项信息,使用name表示名称,value表示值,那么只能是url,password、username、driver;不可以修改 — 就是之前的properties中的内容
    • mappers: 指定sql映射文件的位置,使用一个mapper标签指定mapper文件的位置,整个路径是从类路径开始写, 也就是maven编译后从target/classes目录下开始; 这里要将资源给放进来需要在pom.xml中配置资源插件

 <environments default="myenv"> default必须和某个数据库的配置的id相同,告诉mybatis使用哪个数据库的连接信息
        <environment id="myenv">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/cfengbase?servertimezone=GMT%2B8"/>
                <property name="username" value="cfeng"/>
                <property name="password" value="********"/>
            dataSource>
        environment>
    environments>
	<mappers>
        <mapper resource="cfeng\dao\StudentDao.xml"/>
    mappers>

如果有其他的mapper文件,也可以再次指定;因为environment可以有多个,这里有其他库的操作的mapper文件

Mybatis映射文件mapper

  1. mybatis的映射文件是xml格式的,是专门用来写入sql语句的;这个文件也直接放在dao下面,和表的操作的接口在同一级目录;文件名称和接口名一致;mybatis会执行这些sql
//最上面就是指定这是xml,并且字符的编码方式

  1. 接下来就是指定约束的文件 mybatis-3-mapper.dtd 扩展名是dtd

​ 约束文件的作用 : 限制、检查在当前文件中出现的标签,属性必须符合mybatis的要求

DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

​ 这样就指定了约束文件的位置

  1. 接下来就是mapper就是当前文件的根标签;namespace是为命名空间,是字符串的形式,使用使用的dao接口的全限定名称
<mapper namespace="cfeng.dao.StudentDao">
    <select id="selectStudents" resultType="cfeng.entity.Student">
        SELECT  stuho,stuname,stuclass FROM  student ORDER BY stuno;
    select>
mapper>
  1. 在这个mapper标签中就可以使用特定的标签来表示sql语句,namespace指定了操作的表的映射dao;
    • < select> 表示执行查询操作,select语句
    • < update> 表示修改数据的操作,就是下的update语句
    • < delete> 表示的是删除数据的操作,执行delete语句
    • < insert> 表示增加语句,执行insert语句

select标签

select标签中就直接写select的sql语句就可以正常执行

<select id="selectStudents" resultType="cfeng.entity.Student">
    SELECT  stuho,stuname,stuclass FROM  student ORDER BY stuno;
select>

这个标签表示执行select语句;其中

  • id : 表示的是要执行的sql语句的唯一标识;mybatis会使用这个id来找到要执行的sql语句; 可以自定义,这个id就是接口中方法的名称
  • resultType:标识这条sql语句执行后得到的resultset;遍历这个reult结果集得到的java对象的类型,值为类的全限定名称;也就是表中每一行记录的表示的含义

mybatis简单使用

首先就是下载Mybatis,直接到GitHub上面下载即可;第一个是压缩包–核心文件jar;第二个是项目的源代码;打开就可以发现就是基于Maven的项目;核心的jar包就一个,但是依赖的jar比较多

使用mybatis的关键步骤就是添加mybatis依赖、编写sql映射文件和主配置文件

这里演示一下mybatis的使用;查询student表

数据库表创建

这里第一个简单的例子就是操作简单的表,这里就使用cfengbase中的student表

mysql> DESC student;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| stuno    | int          | NO   | PRI | NULL    | auto_increment |
| stuname  | varchar(255) | YES  |     | NULL    |                |
| stuclass | varchar(255) | YES  |     | NULL    |                |
+----------+--------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)

mysql> SELECT * FROM student;
+-------+---------+----------+
| stuno | stuname | stuclass |
+-------+---------+----------+
|     1 | 张三    | HC2001   |
|     2 | 李四    | HC2002   |
|     3 | Cfeng   | HC2002   |
|     4 | 王五    | HC2001   |
|     6 | Jning   | HC2001   |
|     7 | 里斯    | HC2003   |
|     8 | 卡夫卡  | HC2004   |
|    10 | 里仁    | HC2004   |
|    11 | 小欢    | HC2006   |
+-------+---------+----------+
9 rows in set (0.01 sec)

之后就在IDEA中新建一个新的module来操作这张表;创建一个普通的java SE项目即可,使用quickstart模板

加入maven依赖【坐标】,mysql驱动坐标

为了能够在项目中使用相关的功能和方法,所以必须加入依赖; 加入后maven就会自动下载了

<dependency>
    <groupId>org.mybatisgroupId>
    <artifactId>mybatisartifactId>
    <version>3.5.9version>
dependency>
    
<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>8.0.27version>
dependency>

这里如果导入不成功,可以再reImport一下

这里可以在ponm中加入资源插件,因为mapper文件在java目录下,不再resources中;同时加入之后,还要再加上resources的插件,因为不会保留原来的自动机制

 <build>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.properties
          **/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
      <!-- 还要配置上原来编译资源的路径-->
      <resource>
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*.*
        
        false
      
    
  

创建entity保存表中的数据

entity是对应的是数据库中的一张表

这里因为键盘的关系快捷键Alt + Insert不方便,可以进行修改,修改方式就是File–> settings---->Keymap—>输入new ,鼠标右键,然后点击add,就可以修改了,这里就改为了Alt + L;applyu就可以了【eclipse是ctr + N】

这里新建类改为了 Alt + L

生成代码generate改为 : ALt + G

这里生成代码构造器,要按住ctr 才能选择多个属性

所以这里就快捷生成一个实体类

package cfeng.entity;

public class Student {
    private  int stuid;
    private  String stuname;
    private  String stuclass;

    public Student() {
    }

    public Student(int stuid, String stuname, String stuclass) {
        this.stuid = stuid;
        this.stuname = stuname;
        this.stuclass = stuclass;
    }

    public int getStuid() {
        return stuid;
    }

    public void setStuid(int stuid) {
        this.stuid = stuid;
    }

    public String getStuname() {
        return stuname;
    }

    public void setStuname(String stuname) {
        this.stuname = stuname;
    }

    public String getStuclass() {
        return stuclass;
    }

    public void setStuclass(String stuclass) {
        this.stuclass = stuclass;
    }

    @Override
    public String toString() {
        return "Student{" +
                "stuid=" + stuid +
                ", stuname='" + stuname + '\'' +
                ", stuclass='" + stuclass + '\'' +
                '}';
    }
}

创建Dao接口和impl

创建持久层的接口,定义操作数据库的方法

首先创建一个操作student的Dao接口

package cfeng.dao;

import cfeng.entity.Student;

import java.util.List;

/**
 * 这个接口用于声明操作Student表的方法
 */
public interface StudentDao {

    //查询Student表的所有数据
    public List<Student> selectStudents();
}

之后创建其实现类

创建mybatis使用的配置文件【sql mapper xml】

这个文件是sql的映射文件,mybatis的官方文档中定义了使用的方式,一般一个表对应一个sql映射文件,这个文件就是xml格式的文件【携带数据—sql语句】

这个文件一般放在dao下面,也就是和dao的接口所在的目录相同,因为一个到对应的是一张表,而一个mapper映射文件也是对应的是这张表相关操作文件名和接口名一致

这个文件可以参考mybatis给得帮助文档





DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cfeng.dao.StudentDao">
    <select id="selectStudents" resultType="cfeng.entity.Student">
        SELECT  stuho,stuname,stuclass FROM  student ORDER BY stuno;
    select>
mapper>

这样就写好了上面的dao接口声明的查询所有的学生的sql语句;写入到mapper文件

创建mybatis的主配置文件

一个项目就一个主配置文件,这个文件中提供了数据库的连接信息和sql映射文件的位置信息【其实就是利用反射机制将其剥离出来,易扩展修改】 ----> ResourceBoundle

 <environments default="myenv"> 
        <environment id="myenv">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/cfengbase?servertimezone=GMT%2B8"/>
                <property name="username" value="cfeng"/>
                <property name="password" value="********"/>
            dataSource>
        environment>
    environments>
	<mappers>
        <mapper resource="cfeng\dao\StudentDao.xml"/>
    mappers>

创建测试类【DaoImpl使用JDBC,所以也使用Mybatis】

通过mybatis访问数据库;经过上面的步骤,已经能够访问数据库了,接下来就编写mybatis类来执行mysql语句;类路径的根就是target/classes,那么如何使用mybatis类访问数据库呢?

  1. 定义mybatis主配置文件的名称,从类的根路径开始并使用resources的方法getResource来读取文件
  2. 创建SqlsessionFactoryBuiler对象【sql会话工厂创建对象】,然后使用实例方法build创建一个SqlsessionFactory【sql会话工厂】,然后使用实例方法openSession方法获取一个sqlSession对象
  3. 指定要执行的sql语句的标识,sql映射文件的namespace + “.” + 标签的id值; 刚好就对应的是dao.方法
  4. 执行sql语句,通过sqlid, 使用sqlsession对象的selsectList方法就可以执行【返回值就是List集合】
package cfeng;

import cfeng.entity.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class myApp {

    public static void main(String[] args) throws IOException {
        //读取主配置文件;使用mybatis的Resources的类方法get可以加载文件 ---- 这个stream交给sessionFactory
        String config = "mybatis.xml";
        InputStream in = Resources.getResourceAsStream(config);
        //通过主配置文件的流建立工厂,然后使用工厂创建一个session对象;这个session对象就可以执行sql语句
        SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
        //指定要执行的sql语句,并执行
        String sql = "cfeng.dao.StudentDao.selectStudents";
        List<Student>  list =  session.selectList(sql);
        //操作对象
        list.forEach(System.out::println);
        //list.forEach(stu -> System.our.println(out));
        //关闭sql会话对象
        session.close();
    }
}

这里最后操作集合使用的是forEach;里面是函数式接口,所以使用Lambda表达式或者方法引用都可以mybatis要求实体类的属性和表的col必须一致,像这里stuid和no不一致,赋值就是默认值;不会给stuno

Student{stuid=0, stuname='小欢', stuclass='HC2006'} -----> mybatis要求实体类的属性和表的col必须一致,像这里stuid和no不一致,赋值就是默认值;不会给stuno
Student{stuno=1, stuname='张三', stuclass='HC2001'}
Student{stuno=2, stuname='李四', stuclass='HC2002'}
Student{stuno=3, stuname='Cfeng', stuclass='HC2002'}
Student{stuno=4, stuname='王五', stuclass='HC2001'}
Student{stuno=6, stuname='Jning', stuclass='HC2001'}
Student{stuno=7, stuname='里斯', stuclass='HC2003'}
Student{stuno=8, stuname='卡夫卡', stuclass='HC2004'}
Student{stuno=10, stuname='里仁', stuclass='HC2004'}
Student{stuno=11, stuname='小欢', stuclass='HC2006'}

Process finished with exit code 0

可以看到完美的输出了结果,将每一个对象都是toString

其实这里一个mapper配置文件就是对应Dao中的一个接口;一个接口对应的一个表; 之前没有mybatis就要创建实体类来实现这个方法,但是方法中真正起作用的是sql语句;所以封装就相当于用xml文件实现了接口,其中的方法就是一个简单的sql语句

这样简单就完成了一个查询所有学生的操作;之后再写一个插入学生的操作;像这种的DML操作在JDBC中就会返回一个int型的值标识影响的行数;之前的时候一般就会接收这个值用来判断,这里也是定义这样的一个方法

//插入新的数据 int为影响数据库的行数
public  int insertStudent(Student student);

之后相当于是实现这个方法,在mapper文件中进行配置

    
    <insert id="insertStudent">
        INSERT INTO student (stuno,stuname,stuclass) VALUES (#{stuno},#{stuname},#{stuclass});
    insert>

这里要实现动态sql;使用的是域操作;在maven的pom文件中使用的是类似简化EL的格式${属性名}来获取property标签的值; 这里相当于是获取Stuent类的属性; 这里是#{属性名} —>要求和前面的查询名一致

之后再Myapp类下面使用session来执行这个方法

但是这样子非常麻烦,所以可以直接再test中的java文件夹下编写测试代码测试功能

package cfeng;

import cfeng.entity.Student;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

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

public class TestMybatis {
    //测试方法,测试功能
    @Test
    public  void  testInsert() throws IOException {
        //读取主配置文件;使用mybatis的Resources的类方法get可以加载文件 ---- 这个stream交给sessionFactory
        String config = "mybatis.xml";
        InputStream in = Resources.getResourceAsStream(config);
        //通过主配置文件的流建立工厂,然后使用工厂创建一个session对象;这个session对象就可以执行sql语句
        SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();
        //指定要执行的sql语句,并执行;这里要插入一个学生对象
        Student stu = new Student(12,"黄某","HC2001");
        String sql = "cfeng.dao.StudentDao.insertStudent";
        int count = session.insert(sql,stu);
        System.out.println("影响的行数为 :" + count);
    }
}

这里出现了一个问题;最开始的时候一直Error:(3, 24) java: 程序包org.junit不存在” ---- 这里是因为版本号不匹配的问题;不应该使用默认的4.11;使用之前在maven中测试成功的4.13.2版本即可

影响的行数为 :1

Process finished with exit code 0

这里再看一下数据库: 插入的数据并没有进入数据库

mybatis默认关闭自动提交事务

之前写JDBC的时候,当时做DML操作的时候都会关闭事务的自动提交机制;同时开启事务,当时常犯的错误就是最后忘记commit;导致数据执行不成共;Mybatis也是默认不会自动提交事务的,需要进行手动提交事务

使用sqlSession对象的commit方法就提交事务

加入到上面的程序中,再次查询数据库;可以发现数据插入成功

|    12 | 黄某    | HC2001   |

这个时候,可以再执行一次,看是否会报错,以为主键约束

### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '12' for key 'student.PRIMARY'

可以看到预期结果相同,现在是提交了事务的

看到下面的提示

### SQL: INSERT INTO student (stuno,stuame,stuclass) VALUES (?,?,?);

这虽然是报错的,但是发现其实还是之前的preparedStatement的预编译sql语句的格式;mybatis给封装了

开启日志

上面的操作虽然知道其执行成功了,但是并不知道sql语句的内容等信息,那么要如何知道呢;这里就可以通过开启日志来获取信息;开启日志的方式很简单;

直接在mybatis.xml文件也就是主配置文件中加入日志配置即可,这样就可以输出执行的sql语句和参数; 配置settings标签; Settings: 控制mybatis的全局行为;而这个

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
settings>

注意这里的写法,一旦单词写错就会报错,value为STDOUT_LOGGING — 之前少写了一个G;

之后执行就发现可以

Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections. ---->就是数据库连接池
Opening JDBC Connection
Created connection 988850650.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3af0a9da]    就是关闭连接---连接池就是之前的减少时间在最开始打开20个通道是相同的,这样就可以节省时间
==>  Preparing: INSERT INTO student (stuno,stuname,stuclass) VALUES (?,?,?);
==> Parameters: 13(Integer), 奥利(String), HC2002(String)

这里不要随便rebuild项目,rebuild之后不识别harmcest-core依赖,所以就重新加了一下本地仓库的jar‍♂测试发现rebuild之后就是相当于初始化,所以这个时候之前添加的依赖的内存就没有了,这个时候在pom.xml位置选择Reimport就可以了

Mybatis主要类

Resources

这个类就是读取主配置文件的时候要使用其类方法;其是mybatis中的一个类,负责读取主配置文件

InputStream in = Resources.getResourceASStream(“mybatis.xml”); — 就是编译后的target/classes目录下

SqlSessionFactoryBuilder

这个类唯一的作用就是使用build方法【方法传入的参数就是上面的输入流】来build一座sqlSessionFactiory

SqlSessionFactory

这是一个重量级对象;程序创建一个对象耗时比较长,使用资源较多,在一个项目创建一个就可以了;【像之前的connection也是重量级的对象,所以必须使用pool来减少时间】

public interface SqlSessionFactory {
    SqlSession openSession();//非自动提交事务的

    SqlSession openSession(boolean var1);//这个的参数就是是否开启自动提交机制true就是自动

    SqlSession openSession(Connection var1);

    SqlSession openSession(TransactionIsolationLevel var1);

    SqlSession openSession(ExecutorType var1);

    SqlSession openSession(ExecutorType var1, boolean var2);

    SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);

    SqlSession openSession(ExecutorType var1, Connection var2);

    Configuration getConfiguration();
}

这就是要给接口,其实现类有两个:

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
public class SqlSessionManager implements SqlSessionFactory, SqlSession {

通过作用就是获取SqlSession对象;使用的就是工厂模式

所以如果想要自动提交,就SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession(true);z这个对象和之前的Connection对象相同都有操作数据库的事务方法rollback

SqlSession

SqlSession接口,定义了很多操作数据库的方法

public interface SqlSession extends Closeable {
    <T> T selectOne(String var1);

    <T> T selectOne(String var1, Object var2);

    <E> List<E> selectList(String var1);

    <E> List<E> selectList(String var1, Object var2);

    <E> List<E> selectList(String var1, Object var2, RowBounds var3);

    <K, V> Map<K, V> selectMap(String var1, String var2);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3);

    <K, V> Map<K, V> selectMap(String var1, Object var2, String var3, RowBounds var4);

    <T> Cursor<T> selectCursor(String var1);

    <T> Cursor<T> selectCursor(String var1, Object var2);

    <T> Cursor<T> selectCursor(String var1, Object var2, RowBounds var3);

    void select(String var1, Object var2, ResultHandler var3);

    void select(String var1, ResultHandler var2);

    void select(String var1, Object var2, RowBounds var3, ResultHandler var4);

    int insert(String var1);

    int insert(String var1, Object var2);

    int update(String var1);

    int update(String var1, Object var2);

    int delete(String var1);

    int delete(String var1, Object var2);

    void commit();

    void commit(boolean var1);

    void rollback();

    void rollback(boolean var1);

    List<BatchResult> flushStatements();

    void close();

    void clearCache();

    Configuration getConfiguration();

    <T> T getMapper(Class<T> var1);

    Connection getConnection();
}

可以看到有很多方法,都是操作数据库的,包括执行sql语句和操作事务;这里其默认的实现类为DefaultSqlSession;需要注意的是: SqlSession不是线程安全的,所以需要在方法的内部使用,在执行sql语句前,使用openSession获得SqlSession对象;使用结束后,需要关闭它,执行sqlSession.close():

Mybatis的工具类

虽然发现Mybatis确实简化了很多JDBC的操作,但是按照上面的写法,还是有重复的部分,也就是

 String config = "mybatis.xml";
 InputStream in = Resources.getResourceAsStream(config);
 //通过主配置文件的流建立工厂,然后使用工厂创建一个session对象;这个session对象就可以执行sql语句
 SqlSession session = new SqlSessionFactoryBuilder().build(in).openSession();

这段代码至少可以封装起来,之后就直接调用即可,所以这里创建一个工具类MybatisUtil类同时这里限制只创建一个SqlSessionFactory对象,所以可以参照JDBC工具类,使用static块

package cfeng.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 MybatisUtil {
    private static SqlSessionFactory factory = null;

    static {
        String config = "mybatis.xml";
        try {
            InputStream in = Resources.getResourceAsStream(config);
            //创建SqlSessionFactory对象
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //获取SqlSession对象
    public static SqlSession getSqiSession() {
        SqlSession session = null;
        if(factory != null) {
            session = factory.openSession(false);
        }
        return session;
    }
}

这里表明只是创建一次;只是执行一次的代码可以使用静态代码块的方式;单例模式也是只执行一次;

那么使用工具类,现在的myApp

package cfeng;

import cfeng.entity.Student;
import cfeng.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.io.IOException;
import java.util.List;

public class myApp {

    public static void main(String[] args) throws IOException {

        SqlSession session = MybatisUtil.getSqiSession();
        //指定要执行的sql语句,并执行
        String sql = "cfeng.dao.StudentDao.selectStudents";
        List<Student>  list =  session.selectList(sql);
        //操作对象
        list.forEach(System.out::println);
        //list.forEach(stu -> System.our.println(out));
        //关闭sql会话对象
        session.close();
    }
}

感觉没多大变化,但是最重要的一点就是彻底限制只创建一个factory对象;这样操作人员就没有创建多个占用资源的可能;Util最主要的作用就是static限制只创建一个factory

这里发现了一个问题,那就是创建的Dao接口没有起到任何的作用,因为只是给了xml文件一个参考,但是没有发挥任何的实际作用,以前Dao接口发挥作用是因为会写实现类,实现类中使用JDBC连接数据库进行操作,可是现在不需要dao的实体类了,那么Dao接口的意义?

DaoImpl使用Mybatis

这里使用mybatis配置的的文件要重复写有点麻烦,那也可以借助IDEA工具封装起来;如何封装?
File--->Settings--->File and Code Templates 然后点击+号,就可以在创建一个模板;比如这里创建一个mybatis的mapper文件模板



DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="">
    <select id="" resultType="">
        
    select>
mapper>

这样以后新建的时候就可以直接选择新建Mybatis-mapper文件,就有上面的内容了

同理,可以将主配置文件也设置为一个模板,名称就是Mybatis-config


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    settings>
    
    <environments default="myenv">
        <environment id="myenv">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/***?servertimezone=GMT%2B8"/>
                <property name="username" value=""/>
                <property name="password" value=""/>
            dataSource>
        environment>
    environments>

    <mappers>
        
        <mapper resource=""/>
    mappers>
configuration>

这里就创建实现类即可;在Dao包下面创建子包Impl,然后创建实现类

package cfeng.dao.Impl;

import cfeng.dao.StudentDao;
import cfeng.entity.Student;
import cfeng.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.util.List;

public class StudentDaoImpl implements StudentDao {
    @Override
    public List<Student> selectStudents() {
        SqlSession session = MybatisUtil.getSqiSession();
        //执行sql语句
        String sql = "cfeng.dao.StudentDao.selectStudents";
        List<Student> list =  session.selectList(sql);
        session.close();
        return list;
    }

    @Override
    public int insertStudent(Student student) {
        SqlSession session = MybatisUtil.getSqiSession();
        String sql = "cfeng.dao.StudentDao.insertStudent";
        int count = session.insert(sql,student);
        session.commit();
        session.close();
        return  count;

    }
}

这里就实现了方法;上面因为只是测试是否正常所以才没有使用Dao的Impl;测试就不需要单独创建测试类了;直接在Test下面进行测试

package cfeng;

import cfeng.dao.Impl.StudentDaoImpl;
import cfeng.dao.StudentDao;
import cfeng.entity.Student;
import org.junit.Test;

import java.util.List;

public class TestStudentDaoImpl {

    @Test
   public  void  testSelectStudents(){
        StudentDao dao = new StudentDaoImpl();
       //dao对象,类型就是StudentDao,和namespace是相同的;而这个方法名称就是mapper文件中的id的值
       //通过下面的方法调用就可以获得sql语句的定位
        List<Student> list = dao.selectStudents();
        list.forEach(System.out::println);
    }

    @Test
    public void  testInsertStudent(){
        StudentDao dao = new StudentDaoImpl();
        Student student = new Student(14,"新亚","HC2003");
        int count = dao.insertStudent(student);
        System.out.println(count);
    }

}
非常方便就测试出dao的方法是正确的

分析Dao中的代码,发现前面的几行代码都是相同的,还有关闭等也是相同的;那么如何简化这种操作呢? 其中最核心的就是拿到sql语句,也就是通过namespace和id在mapper文件中获取到的sql语句

//dao对象,类型就是StudentDao,和namespace是相同的;而这个方法名称就是mapper文件中的id的值
//通过下面的方法调用就可以获得sql语句的定位 ---- 所以说这里的sql获取是可以简化的

  • 所以得到的结论就是: 通过dao的方法调用程序就能确定mybatis要调用的sqlsession的方法,如果返回值为List,调用的就是selectStudents方法;如果返回值为int,调用的就是inert方法,通过namespace和id定位过去识别标签;< insert> < update>……等就会调用相关的方法; — 完成了动态的使用

Mybatis的动态代理session.getMapper

Mybatis根据dao的方法调用,获取执行sql语句的信息; 所以之前强调namespace和id的命名必须和Dao的类全限定名和方法名一致,运作的方式就是Mybatis根据Dao接口,创建一个Dao接口的实现类,并创建这个类的对象,完成SqlSession调用方法,访问数据库–其实就是

programmer不需要写上面的DaoImpl类,Mybatis通过JDK动态代理自动创建接口的实现类完成方法的调用;mybatis帮助写了DaoImpl类

强调namespace和id的命名必须和Dao的类全限定名和方法名一致!!!! ----- 不然无法通过反射获取到信息;所以现在不需要Impl了,直接删掉

//现在获得dao的方式
/**
  * 使用mybatis的动态代理机制,使用session.getMapper(dao接口)
  * getMapper能获取Dao接口对应的实现类对象,也就是DaoImpl的实例对象
  */
StudentDao dao = MybatisUtil.getSqiSession().getMapper(StudentDao.class);

不需要创建对象了,动态代理已经创建好一个对象了,之后会手动给大家实现以下这个动态代理;【所以说这个也是Mybatis的一个重要的变革,那就是不再需要写实现类了,真的就只用在配置文件中写sql语句就可以了,最多再写一个接口】

可以再测试以下

package cfeng;

import cfeng.dao.StudentDao;
import cfeng.entity.Student;
import cfeng.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class TestStudentDaoImpl {

    @Test
   public  void  testSelectStudents(){
        /**
         * 使用mybatis的动态代理机制,使用session.getMapper(dao接口)
         * getMapper能获取Dao接口对应的实现类对象,也就是DaoImpl的实例对象
         */
        StudentDao dao = MybatisUtil.getSqiSession().getMapper(StudentDao.class);
        List<Student> list = dao.selectStudents();
        list.forEach(System.out::println);
    }

    @Test
    public void  testInsertStudent(){
        StudentDao dao = MybatisUtil.getSqiSession().getMapper(StudentDao.class);
        Student student = new Student(14,"新亚","HC2003");
        int count = dao.insertStudent(student);
        System.out.println(count);
    }
}
但是工具类还是需要的,创建一个session

所以代理的就是接口中所有的功能,这个代理类就可以看作就是接口的实现类;所以前天的JDK动态代理,不需要工厂的,商店代理类其实主要代理的就是那个买雪糕的接口;emmm……JDK动态代理

你可能感兴趣的:(JAVAweb,hibernate,java,spring,mybatis)