MyBatis学习笔记(一)MyBatis介绍、入门案例以及自定义实现MyBatis

一、MyBatis介绍、入门案例以及自定义实现MyBatis

1、什么是框架?

它是我们软件开发中的一套解决方案,不同的框架解决的是不同的问题。
使用框架的好处:
框架封装了很多的细节,使开发者可以使用极简的方式实现功能。大大提高开发效率。

2、三层架构

在这里插入图片描述

  • 表现层:是用于展示数据的
  • 业务层:是处理业务需求(Spring不属于任何一个层)
  • 持久层:是和数据库交互的

3、持久层技术解决方案

JDBC技术:

  • Connection
  • PreparedStatement
  • ResultSet

Spring的JdbcTemplate:Spring中对jdbc的简单封装
Apache的DBUtils:它和Spring的JdbcTemplate很像,也是对Jdbc的简单封装
以上这些都不是框架
JDBC是规范、
Spring的JdbcTemplate和Apache的DBUtils都只是工具类

4、mybatis的概述

mybatis是一个持久层框架,用java编写的。
它封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程
它使用了ORM思想实现了结果集的封装。

ORM:
Object Relational Mappging 对象关系映射。简单的说,就是把数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表。
user User
id userId
user_name userName
今天我们需要做到
实体类中的属性和数据库表的字段名称保持一致。
user User
id id
user_name user_name

5、mybatis的入门

mybatis的环境搭建
demo的基本架构

在这里插入图片描述

第一步:创建maven工程并导入坐标

第二步:创建实体类和dao的接口

第三步:创建Mybatis的主配置文件 sqlMapConfig.xml

<?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">
<!--mybatis的主配置文件-->
<configuration>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql的环境-->
        <environment id="mysql">
            <!--配置的事物类型-->
            <transactionManager type=""></transactionManager>
            <!--配置数据源(连接池)-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql//localhost:3307/mybatis1029"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>

        </environment>
    </environments>

    <!--指定映射配置文件的位置,指的是每个dao独立的-->
    <mappers>
        <mapper resource="com.sc.dao.IUserDao"/>
    </mappers>
</configuration>

第四步:创建映射配置文件 IUserDao.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">

 <!--namespace指的是你的dao接口-->
 <mapper namespace="com.sc.dao.IUserDao">
    <!--配置查询所有-->
    <!--其中id不能乱写必须是dao接口中的方法  resultType写的是实体类的全路径-->
    <select id="findAll" resultType="com.sc.dao.IUserDao">
        select * from user;
    </select>

</mapper>

环境搭建的注意事项:

第一个:创建IUserDao.xml 和 IUserDao.java时名称是为了和我们之前的知识保持一致。
在Mybatis中它把持久层的操作接口名称和映射文件也叫做:Mapper
所以:IUserDao 和 IUserMapper是一样的
第二个:在idea中创建目录的时候,它和包是不一样的
包在创建时:com.itheima.dao它是三级结构
目录在创建时:com.itheima.dao是一级目录
第三个:mybatis的映射配置文件位置必须和dao接口的包结构相同
第四个:映射配置文件的mapper标签namespace属性的取值必须是dao接口的全限定类名
第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名

当我们遵从了第三,四,五点之后,我们在开发中就无须再写dao的实现类
mybatis的入门案例

第一步:读取配置文件

第二步:创建SqlSessionFactory工厂

第三步:创建SqlSession

第四步:创建Dao接口的代理对象

第五步:执行dao中的方法

第六步:释放资源

//1.读取配置文件
InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
//2.创建SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
SqlSession session = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用代理对象执行方法
List<User> users = userDao.findAll();
for (User user:users) {
    System.out.println(user);
}
//6.释放资源
session.close();
in.close();

注意事项:
不要忘记在映射配置中告知mybatis要封装到哪个实体类中
配置的方式:指定实体类的全限定类名

mybatis基于注解的入门案例:
把IUserDao.xml移除,在dao接口的方法上使用@Select注解,并且指定SQL语句
同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定dao接口的全限定类名。
明确:
我们在实际开发中,都是越简便越好,所以都是采用不写dao实现类的方式。
不管使用XML还是注解配置。
但是Mybatis它是支持写dao实现类的。

测试类

public class MybatisTest {

    public static void main(String[] args) throws Exception {
        //1.读取配置文件
        InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
		//2.创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(inputStream);
		//3.使用工厂创建SqlSession
        SqlSession session = factory.openSession();
		//4.使用SqlSession创建dao接口的代理对象
        IUserDao userDao =session.getMapper(IUserDao.class);
		//5.使用代理对象的方法
        List<User> users = userDao.findAll();
        for (User user:users
             ) {
            System.out.println(user);
        }
		//6.释放资源
        session.close();
        inputStream.close();
    }
}

入门案例使用到的设计模式:

在这里插入图片描述

自定义Mybatis的分析

在这里插入图片描述

如果使用了Dao.Impl:
在这里插入图片描述

在这里插入图片描述

mybatis在使用代理dao的方式实现增删改查时做什么事呢?
只有两件事:

  • 创建代理对象
  • 在代理对象中调用selectList

查询所有session.selectList的分析:

在这里插入图片描述

session.getMapper(IUserDao.class)是怎么创建代理的呢?

创建代理对象的分析:在这里插入图片描述
代理对象:

  • 类加载器:要代理谁,就用谁的类加载器
  • 代理对象要实现的接口:和被代理对象实现相同的接口
  • 如何代理:增强的方法——invocationHandler接口,在实现类中调用selectList方法

自己定义mybatis:

自定义mybatis能通过入门案例看到类
class Resources:

class SqlSessionFactoryBuilder:

interface SqlSessionFactory:

interface SqlSession:

XMLBuilder工具类:

把mybatis的坐标删了

XML实现:
  1. resource类读取配置文件,读成字节流

    /**
     * 使用类加载器读取配置文件的类
     */
    public class Resources {
    
        /**
         * 根据传入的参数,获取一个字节输入流
         * @param filePath
         * @return
         */
        public static InputStream getResourceAsStream(String filePath){
            return Resources.class.getClassLoader().getResourceAsStream(filePath);
        }
    }
    
  2. 读取出的信息,使用工具类XMLBuilder封装到Configuration参数类中

    setMappers方法要特别注意在这里插入图片描述

    /**
     * 自定义mybatis配置类
     */
    public class Configuration {
    
        private String driver;
        private String url;
        private String username;
        private String password;
        private Map<String,Mapper> mappers = new HashMap<String, Mapper>();
    
        public Map<String, Mapper> getMappers() {
            return mappers;
        }
    	
        //由于还支持别的dao进行mapper映射,setMappers应该是追加而不是覆盖,不然只能配置一个
        public void setMappers(Map<String, Mapper> mappers) {
            //追加而不是覆盖
            this.mappers.putAll(mappers);
        }
    
        //各种getset方法
    }
    

    交给构建者SqlSessionFactoryBuilder

    /**
     * 用于创建一个SqlSessionFactoryBuilder对象
     */
    public class SqlSessionFactoryBuilder {
    
        /**
         * 根据参数的字节输入流来构建一个SqlSessionFactory工厂
         * @param config
         * @return
         */
        public SqlSessionFactory build(InputStream config){
    
            Configuration cfg = XMLConfigBuilder.loadConfiguration(config);
            return new DefaultSqlSessionFactory(cfg);
        }
    }
    

    构建者构建了工厂对象——SqlSessionFactory接口的实现类DefaultSqlSessionFactory

    /**
     * SqlSessionFactory接口的实现类
     */
    public class DefaultSqlSessionFactory implements SqlSessionFactory {
    
        private Configuration cfg;
        public DefaultSqlSessionFactory(Configuration cfg){
            this.cfg = cfg;
        }
    	
        /**
         * 用于创建一个新的操作数据库对象
         * @return
         */
        @Override
        public SqlSession openSession() {
            return new DefaultSqlSession(cfg);
        }
    }
    
  3. 工具类DataSourceUtil用于创建Connection

    /**
     * 用于创建数据源的工具类
     */
    public class DataSourceUtil {
    
        /**
         * 用于获取一个连接
         * @param cfg
         * @return
         */
        public static Connection getConnection(Configuration cfg){
            try{
                Class.forName(cfg.getDriver());
                return DriverManager.getConnection(cfg.getUrl(),cfg.getUsername(),cfg.getPassword());
    
            }catch (Exception e){
                throw new RuntimeException(e);
            }
    
        }
    
    }
    

    Mapper对象封装sql和结果的全限定名

    /**
     * 用于封装sql和结果的全限定类名
     */
    public class Mapper {
    
        private String queryString;
        private String resultType;//实体类的全限定类名
    
        public String getQueryString() {
            return queryString;
        }
    
        public void setQueryString(String queryString) {
            this.queryString = queryString;
        }
    
        public String getResultType() {
            return resultType;
        }
    
        public void setResultType(String resultType) {
            this.resultType = resultType;
        }
    }
    

    工厂对象openSession给我们提供了SqlSession接口的实现DefaultSqlSession,在这个里面实现创建代理对象和查询所有的操作。

    /**
     * SqlSession接口的实现
     */
    public class DefaultSqlSession implements SqlSession {
    
        private Configuration cfg;
        private Connection connection;
    
        public DefaultSqlSession(Configuration cfg){
            this.cfg = cfg;
            connection = DataSourceUtil.getConnection(cfg);
        }
    
        /**
         * 创建代理对象
         * @param daoInterfaceClass dao的接口字节码
         * @param <T>
         * @return
         */
        @Override
        public <T> T getMapper(Class<T> daoInterfaceClass) {
            //强制转换不要忘了
            return (T)Proxy.newProxyInstance(daoInterfaceClass.getClassLoader(),
                    new Class[]{daoInterfaceClass},new MapperProxy(cfg.getMappers(),connection));
        }
    
        /**
         * 释放资源
         */
        @Override
        public void close() {
            if (connection != null)
            try {
                connection.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    其中MapperProxy即为代理对象,执行Executor工具类的selectList方法进行增强,返回一个List给Session

    public class MapperProxy implements InvocationHandler {
    
        //map的key是全限定类名+方法名
        private Map<String,Mapper> mappers;
        private Connection conn;
    
        public MapperProxy(Map<String,Mapper> mappers,Connection conn){
            this.mappers = mappers;
            this.conn = conn;
        }
    
        /**
         * 用于对方法进行增强,就是调用selectList方法
         * @param proxy
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //1.获取方法名
            String methodName = method.getName();
            //2.获取类名
            String className = method.getDeclaringClass().getName();
            //3.组合
            String key = className+"."+methodName;
            //4.获取mappers中的mapper对象
            Mapper mapper = mappers.get(key);
            if (mapper == null){
                throw new IllegalArgumentException("传入参数有误");
            }
            //调用
            return new Executor().selectList(mapper,conn);
        }
    }
    

    最终就是Dao调用getMapper方法,创建实现类增强

    IUserDao userDao = session.getMapper(IUserDao.class);
    

注解方法类似

注解实现:

SqlMapConfig.xml中mapper格式改成class

<mappers>
    <!--xml <mapper resource="com/itheima/dao/IUserDao.xml"/>-->
    <mapper class="com.sc.dao.IUserDao"/>
</mappers>

Dao接口使用@Select

public interface IUserDao {
    /**
     * 查询所有操作
     * @return
     */
    @Select("select * from user")
    List<User> findAll();
}

创建注解Select.java

@Retention(RetentionPolicy.RUNTIME)//生命周期
@Target(ElementType.METHOD)//出现位置
public @interface Select {

    /**
     * 配置SQL语句的
     * @return
     */
    String value();
}

在工具类中

反射注解填充Map<String,Mapper>对象mappers

private static Map<String,Mapper> loadMapperAnnotation(String daoClassPath)throws Exception{
        //定义返回值对象
        Map<String,Mapper> mappers = new HashMap<String, Mapper>();

        //1.得到dao接口的字节码对象
        Class daoClass = Class.forName(daoClassPath);
        //2.得到dao接口中的方法数组
        Method[] methods = daoClass.getMethods();
        //3.遍历Method数组
        for(Method method : methods){
            //取出每一个方法,判断是否有select注解
            boolean isAnnotated = method.isAnnotationPresent(Select.class);
            if(isAnnotated){
                //创建Mapper对象
                Mapper mapper = new Mapper();
                //取出注解的value属性值
                Select selectAnno = method.getAnnotation(Select.class);
                String queryString = selectAnno.value();
                mapper.setQueryString(queryString);
                //获取当前方法的返回值,还要求必须带有泛型信息
                Type type = method.getGenericReturnType();//List<User>
                //判断type是不是参数化的类型
                if(type instanceof ParameterizedType){
                    //强转
                    ParameterizedType ptype = (ParameterizedType)type;
                    //得到参数化类型中的 实际类型参数 
                    Type[] types = ptype.getActualTypeArguments();
                    //取出第一个
                    Class domainClass = (Class)types[0];
                    //获取domainClass的类名
                    String resultType = domainClass.getName();
                    //给Mapper赋值
                    mapper.setResultType(resultType);
                }
                //组装key的信息
                //获取方法的名称
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String key = className+"."+methodName;
                //给map赋值
                mappers.put(key,mapper);
            }
        }
        return mappers;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值