MyBatis-01

MyBatis-01

一、Mybatis概述

1. Mybatis简介

1.1 JDBC的问题
  1. 硬编码问题
    • 数据库连接信息的硬编码
    • SQL语句和参数硬编码
    • 结果集封装硬编码
  2. 大量的重复代码
1.2 Mybatis介绍

​ Mybatis是一个优秀的Java轻量级 持久层框架。

  • 它内部封装了JDBC,使开发人员只需要关心SQL语句,而不需要处理繁琐的JDBC步骤
  • 它采用了ORM思想,解决了实体和数据库映射的问题。只要提供好sql语句,配置了映射,Mybatis会自动根据参数值动态生成SQL,执行SQL并把结果封装返回给我们。
  • 它支持XML和注解两种方式配置映射。XML方式使用的更多
1.3 ORM思想

​ ORM:Object Relational Mapping,对象关系映射思想。指把Java对象和数据库的表和字段进行关联映射,从而达到操作Java对象,就相当于操作了数据库。

二、Mybatis快速入门

1. Mybatis下载

2. Mybatis快速入门的准备工作

​ 快速入门的功能需求:查询所有用户信息,得到List<User>

  1. 初始化数据库:执行数据库脚本《资料/mybatisdb.sql》

  2. 准备开发环境:jdk1.8, Maven和本地仓库,idea

  3. 准备好Mybatis需要的配置文件约束

    • Mybatis核心配置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映射配置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">
    

3. 快速入门案例(重点)

3.1 创建Maven的java项目:

​ 因为只涉及Dao层,不涉及客户端,所以只要创建Java项目即可。项目坐标信息如下:

	groupId:         com.viking
	artifactId:      day46_mybatis01_quickstart
	version:         1.0-SNAPSHOT
	packing:         jar
3.2 在pom.xml中添加依赖
    <dependencies>
        <!--Junit单元测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <!--MySql的数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!--Mybatis的jar包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>
        <!--Mybatis依赖的日志包-->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
3.3 创建JavaBean: com.viking.bean.User
public class User {
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;

    //get/set方法......

    //toString方法......
}
3.4 创建dao层的接口:com.viking.dao.IUserDao

​ 在Mybatis里,把dao层的接口称之为映射器(取 调用接口的方法即相当于操作数据库 之意)。

​ 映射器的类名,可以叫XXXMapper,也可以叫IXXXDao。我们这里按照之前的习惯,取名IUserDao

​ 注意:只要创建接口即可,不需要创建接口的实现类

public interface IUserDao {
    List<User> queryAll();  
}
3.5 准备映射配置配置文件xml

注意:

  • 映射配置文件名称要和映射器类名一样。例如:映射器叫IUserDao,那么配置文件就叫IUserDao.xml
  • 映射配置文件的位置也要和映射器一样。例如:映射器在com.viking.dao里,那么配置文件就应该在resources的com/viking/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:给哪个接口配置的映射,写接口的全限定类名-->
<mapper namespace="com.viking.dao.IUserDao">
    <!--select标签:表示要执行查询语句; id:给接口里哪个方法配置的,写方法名;resultType:结果集封装类型-->
    <select id="queryAll" resultType="com.viking.bean.User">
        select * from user
    </select>
</mapper>
3.6 准备Mybatis的日志配置文件

​ Mybatis支持使用log4j输出执行日志信息,但是需要我们提供log4j的配置文件:log4j.properties

注意:

  1. 如果没有log4j.properties,不影响Mybatis的功能,只是没有详细日志而已
  2. 如果需要日志的话,要把log4j.properties文件放到resources目录下。log4j.properties内容如下:
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
3.7 准备Mybatis的核心配置文件xml

注意:

  • 核心配置文件的名称随意,我们习惯叫 SqlMapConfig.xml
  • 核心配置文件的位置随意,我们习惯放到resources目录下
<?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_mybatis">
        <!--定义一个数据库连接环境-->
        <environment id="mysql_mybatis">
            <!--设置事务管理方式,固定值JDBC-->
            <transactionManager type="JDBC"/>
            <!--设置数据源,POOLED,UNPOOLED,JNDI,我们使用POOLED-->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis49"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <!--  !!!配置映射文件的位置!!!!!!!!!!!!!!这一步千万不要忘记!!!!!!!!!!!!!!  -->
    <mappers>
        <mapper resource="com/viking/dao/IUserDao.xml"/>
    </mappers>
</configuration>
3.7 编写测试代码
	@Test
    public void testQuickStart() throws IOException {
        //1. 读取核心配置文件SqlMapConfig.xml
        InputStream is = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2. 创建SqlSessionFactoryBuilder构造者对象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //3. 使用构造者builder,根据配置文件的信息is,构造一个SqlSessionFactory工厂对象
        SqlSessionFactory factory = builder.build(is);
        //4. 使用工厂对象factory,生产一个SqlSession对象
        SqlSession session = factory.openSession();
        //5. 使用SqlSession对象,获取映射器IUserDao接口的代理对象
        IUserDao dao = session.getMapper(IUserDao.class);
        //6. 调用IUserDao代理对象的方法,查询所有用户
        List<User> users = dao.queryAll();
        for (User user : users) {
            System.out.println(user);
        }
        //7. 释放资源
        session.close();
        is.close();
    }

三、自定义Mybatis(了解)

​ 在这一章,我们将要使用前边学习过的知识,尝试自定义一个Mybatis框架出来,以便加深对Mybatis的理解。自定义框架涉及的知识点有:构造者模式、工厂模式、代理模式、反射、注解、XML解析、XPath表达式等等。

1. Mybatis框架使用了哪些设计模式(面试)

《设计模式之禅》

1.1 构造者模式
  • 使用SqlSessionFactoryBuilder,根据核心配置文件,构造一个SqlSessionFactory对象出来
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(is);
  • 构造者模式:用于构造一个对象。它的重点在于 根据配置文件、或者参数,定制一个对象出来
  • 好处:可以把定制对象的复杂过程隐藏起来, 只要调用一个方法,把配置文件/参数传递进去,就可以得到根据参数定制的对象。
1.2 工厂模式
  • 使用SqlSessionFactory,生产一个SqlSession对象出来
SqlSession session = factory.openSession();
  • 工厂模式:用于代替new操作的一种模式,是一种最常用的实例化对象的模式了。
  • 好处:可以降低程序之间的耦合性,提高应用的可扩展性,在功能维护时尽量少的代码修改
1.3 代理模式
  • 使用SqlSession获取IUserDao的代理对象,通过代理对象实现增、删、改、查操作
IUserDao dao = session.getMapper(IUserDao.class);
  • 代理模式:在某些情况下,一个对象不适合或者不能直接调用另外一个对象,就可以使用代理对象作为中介来间接调用。
  • 好处:
    • 职责清晰。被代理对象只要完成自己的业务逻辑,不需要关心其它非本职事务;通过代理对象来完成功能的扩展。附带的后果就是编程简洁、清晰
    • 高扩展性。

2. 自定义Mybatis框架的分析

2.1 JDBC的执行流程
2.2 基于JDBC的封装流程分析

​ Mybatis框架中,最核心的一个对象就是SqlSession对象。这个对象本身提供了执行增、删、改、查的方法,同时也提供了获取接口代理对象的方法,通过代理对象实现增、删、改、查。

​ 我们自定义Mybatis时,最关键的一步也是SqlSession对象。但是SqlSession对象不能直接new出来,需要读取配置文件,并使用一系列的设计模式生产出来。过程如下:

  1. 读取配置文件,封装成Configuration对象
  2. 使用构造者模式、工厂模式 生产出SqlSession对象
  3. SqlSession对象使用代理模式,创建接口的代理对象,由代理对象完成增、删、改、查操作
2.3 自定义Mybatis的准备工作
  1. 创建Maven的Java项目
  2. 引入坐标依赖:引入MySql驱动、Junit、dom4j、jaxen,无需再引入Mybatis和log4j
  3. 准备JavaBean和映射器IUserDao
  4. 准备映射配置文件和核心配置文件
  5. 把快速入门中的测试代码拷贝过来

3. 自定义Mybatis框架的实现

3.1 配置文件的读取和封装
  1. 配置信息相关类介绍
    • Mapper:接口中每个方法的配置信息对象,包含了方法要执行的SQL语句、返回类型
    • Configuration:配置信息对象,包含了数据库连接信息,和所有接口中所有方法的Mapper信息
  2. 创建一个XMLConfigParser类,用于读取配置文件封装成Configuration对象
public class XMLConfigParser {

    public Configuration parse(InputStream is) throws DocumentException, IOException {
        Configuration configuration = new Configuration();

        SAXReader reader = new SAXReader();
        Document document = reader.read(is);
        //1. 读取核心配置文件的信息
        //1.1 读取数据库驱动信息
        Element element = (Element) document.selectSingleNode("//property[@name='driver']");
        String driver = element.attributeValue("value");
        configuration.setDriver(driver);
        //1.2 读取数据库url信息
        element = (Element) document.selectSingleNode("//property[@name='url']");
        String url = element.attributeValue("value");
        configuration.setUrl(url);
        //1.3 读取数据库的username
        element = (Element) document.selectSingleNode("//property[@name='username']");
        String username = element.attributeValue("value");
        configuration.setUsername(username);
        //1.4 读取数据库的password
        element = (Element) document.selectSingleNode("//property[@name='username']");
        String password = element.attributeValue("value");
        configuration.setPassword(password);

        //2. 读取所有的映射配置文件信息
        List<Node> nodes = document.selectNodes("//mappers/mapper");
        for (Node node : nodes) {
            Element mapperElement = (Element) node;
            String mapperResource = mapperElement.attributeValue("resource");
            if (mapperResource != null) {
                Map<String, Mapper> mappers = loadMappers(mapperResource);
                configuration.setMappers(mappers);
            }
        }

        return configuration;
    }

    private Map<String, Mapper> loadMappers(String mapperResource) throws IOException {
        Map<String, Mapper> mappers = new HashMap<>();
        InputStream is = null;
        try {
            is = Resources.getResourceAsStream(mapperResource);

            SAXReader reader = new SAXReader();
            Document document = reader.read(is);
            Element rootElement = document.getRootElement();
            //1. 获取根标签上的namespace属性值-----映射器接口的全限定类名
            String daoClassName = rootElement.attributeValue("namespace");
            //2. 获取接口里每个方法的配置信息
            List<Element> elements = rootElement.elements();
            for (Element element : elements) {
                //2.1 获取方法的名称
                String daoMethodName = element.attributeValue("id");
                //2.2 获取方法的SQL语句
                String sql = element.getText();
                //2.3 获取方法的返回值类型
                String resultType = element.attributeValue("resultType");

                //2.4 把这个方法的SQL语句和返回值类型,封装成一个Mapper对象
                Mapper mapper = new Mapper();
                mapper.setResultType(resultType);
                mapper.setSql(sql);
                //2.5 把Mapper对象放到  Map<String, Mapper> mappers里。key是接口类名+方法名
                String key = daoClassName+"."+ daoMethodName;
                mappers.put(key, mapper);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            is.close();
        }

        return mappers;
    }
}
3.2 DefaultSqlSession对象的创建
  1. 相关类介绍
    • SqlSessionFactoryBuilder:构造者 类,用于根据配置文件,构造工厂对象SqlSessionFactory
    • SqlSessionFactory接口 和 实现类DefaultSqlSessionFactory:工厂 类,用于生产SqlSession对象
    • SqlSession接口 和 实现类DefaultSqlSession: 我们需要的就是这个对象,用于获取代理对象
  2. DefaultSqlSession对象的创建过程分析
    • 通过构造者SqlSessionFactoryBuilder,根据配置文件,使用build方法构造一个工厂对象DefaultSqlSessionFactory
    • 通过工厂DefaultSqlSessionFactory,使用openSession()方法,生产一个DefaultSqlSession对象
  3. 创建SqlSession接口 和 实现类DefaultSqlSessionFactory
public interface SqlSession{
    
}
public class DefaultSqlSession implements SqlSession{
    
}
  1. 创建SqlSessionfactory接口 和 实现类DefaultSqlSessionFactory
public interface SqlSessionFactory{
    
}
public class DefaultSqlSessionFactory implements SqlSessionFactory{
    
}
  1. 创建SqlSessionFactoryBuilder,提供方法build(is)
public class SqlSessionFactoryBuilder{
    public SqlSessionFactory build(is){
        //1. 读取配置文件is的内容,封装成Configuration对象(使用3.2章节中的XMLConfigParser)
        XMLConfigParser parser = new XMLConfigParser();
        Configuration configuration = parser.parse(is);
        //2. 根据configuration对象,构造一个DefaultSqlSessionFactory对象
        return new DefaultSqlSessionFactory(configuration);
    }
}
  1. 修改DefaultSqlSessionFactory的代码
public class DefaultSqlSessionFactory implements SqlSessionFactory{
    private Configuration configuration;
    
    public DefaultSqlSessionFactory(){}
    
    /**
     * 使用构造方法,把configuration对象赋值给成员变量
     * 注意:configuration对象,是给SqlSession使用的,工厂类自己不用
     */
    public DefaultSqlSessionFactory(Configuration configuration){
        this.configuration = configuration;
    }
    
    /**
     * 提供一个生产SqlSession对象的方法
     * 把configuration对象传递给SqlSession对象
     */
    public SqlSession openSession(){
        return new DefaultSqlSession(configuration);
    }
}
  1. 修改DefaultSqlSession的代码
public class DefaultSqlSession implements SqlSession{
    private Configuration configuration;
    
    public DefaultSqlSession(){}
    
    /**
     * 使用构造方法,把configuration对象赋值给成员变量
     */
    public DefaultSqlSession(Configuration configuration){
        this.configuration = configuration;
    }
}
3.3 生成代理对象让代理对象执行SQL封装结果
  1. 修改DefaultSqlSession的代码,增加getMapper方法,用于生成接口的代理对象
public class DefaultSqlSession implements SqlSession{
    private Configuration configuration;
    private Connection connection;
    
    public DefaultSqlSession(){}
    
    /**
     * 使用构造方法,把configuration对象赋值给成员变量
     */
    public DefaultSqlSession(Configuration configuration){
        this.configuration = configuration;
        // 获取连接:本身应该是使用连接池,从连接池中获取。这里简化处理,直接获取连接
        Class.forName(configuration.getDriver());
        this.connection = DriverManager.getConnection(configuration.getUrl(), configuration.getUsername(), configuration.getPassword());
    }
    
    /**
     * 根据dao接口的字节码对象,生成代理对象。
     * 
     */
    public <T> T getMapper(Class daoInterfaceClass){
        return (T) Proxy.newProxyInstance(
        	daoInterfaceClass.getClassLoader(),
            new Class[]{daoInterfaceClass},
            new InvocationHandler(){
                public Object invoke(Object proxy, Method method, Object[] args){
                    //这里写代理对象的行为:使用Executor执行SQL语句。
                    //要使用Executor的selectList方法执行SQL语句,需要的参数:
                    //1. 数据库连接对象--构造方法中已经得到了连接对象 this.connection
                    //2. SQL语句和结果集封装类型--Mapper对象---从Configuration的map里取
                    String key = daoInterfaceClass.getName()+"."+method.getName();
                    Mapper mapper = configuration.getMappers().get(key);
                    
                    return Executor.selectList(connection,mapper);
                }
            }
        );
    }
}
  1. 修改DefaultSqlSession的代码,增加close()方法,用于关闭连接
public class DefaultSqlSession implements SqlSession{
    private Configuration configuration;
    private Connection connection;
    
    public DefaultSqlSession(){}
    
    public DefaultSqlSession(Configuration configuration){
        ..............
    }
    
    public <T> T getMapper(Class daoInterfaceClass){
        ..............
    }
    
    /**
     * 释放资源
     */
    public void close(){
        this.connection.close();
    }
}
3.4 测试自定义的Mybatis

​ 运行测试代码,验证自定义的Mybatis能否正常运行

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值