mybatis源码深入学习-01

如果我们没有mybatis下,怎么用java语言去实现,学一个东西得明白这个东西干嘛用,解决什么问题,有结果推向过程,最后才能更好的明白过程。

1、mybatis干嘛用?解决了什么痛点?

MyBatis是一流的持久性框架,支持自定义SQL,存储过程和高级映射。MyBatis消除了几乎所有的JDBC代码以及参数的手动设置和结果检索。MyBatis可以使用简单的XML或注释进行配置,并将图元,映射接口和Java POJO(普通的旧Java对象)映射到数据库记录。

mybatis是一个orm框架,也就是把字段映射为对象的属性。

2、首先复习下java的jdbc

JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范而不是一个实现,能够执行SQL语句。它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,本文中的代码都是针对MySQL数据库实现的。

JDBC编程步骤
(1)、导入专用的jar包(不同的数据库需要的jar包不同)

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.39</version>
</dependency>

(2)、初始化驱动

  Class.forName("com.mysql.jdbc.Driver");	

(3)、建立JDBC和数据库之间的Connection连接

      String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8";
            String name = "root";
            String password = "123456";
            // 获取数据库连接
          Connection  connection = DriverManager.getConnection(url, name, password);

(4)、创建Statement或者PreparedStatement接口,执行SQL语句
使用Statement接口
Statement中使用字符串拼接的方式,该方式存在句法复杂,容易犯错等缺点,且存在sql注入风险

Statement s = conn.createStatement();
// 准备sql语句
// 注意: 字符串要用单引号'
String sql = "insert into t_courses values(null,"+"'数学')";
//在statement中使用字符串拼接的方式,这种方式存在诸多问题
s.execute(sql);
System.out.println("执行插入语句成功");

使用PreparedStatement接口
PreparedStatement也是用来执行sql语句的与创建Statement不同的是,需要根据sql语句创建PreparedStatement。除此之外,还能够通过设置参数,指定相应的值,而不是Statement那样使用字符串拼接。

  	String sql = "select * from user  where user_name=?";
	// 创建Statement对象(每一个Statement为一次数据库执行请求)
	PreparedStatement preparedStatement = connection.prepareStatement(sql);
    // 设置传入参数
    preparedStatement.setString(1, "zhangsan");

(5)、执行SQL语句

  resultSet = preparedStatement.executeQuery();

(6)、处理查询结果(将查询结果转换成List格式)

ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();

while (resultSet.next()) {
    Map map = new HashMap();
    for (int i = 0; i < columnCount; i++) {
        String columnName = metaData.getColumnName(i + 1);
        map.put(columnName, resultSet.getString(columnName));
    }
    resultList.add(map);
}

(7)、释放资源

/**
	 * 封装三个关闭方法
	 * @param pstmt
	 */
	public static void close(PreparedStatement pstmt){
		if(pstmt != null){						//避免出现空指针异常
			try{
				pstmt.close();
			}catch(SQLException e){
				e.printStackTrace();
			}
			
		}
	}
	
	public static void close(Connection conn){
		if(conn != null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}
	
	public static void close(ResultSet rs){
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}

完整代码

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JdbcTest {
    public static void main(String[] args) {
        List<Map<String, Object>> user = getUser();
        System.out.println(user);
    }

    private static List<Map<String, Object>> getUser() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
        try {
            // 加载JDBC驱动
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            String url = "jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8";
            String name = "root";
            String password = "123456";
            // 获取数据库连接
            connection = DriverManager.getConnection(url, name, password);
            String sql = "select * from user  where user_name=?";

            // 创建Statement对象(每一个Statement为一次数据库执行请求)
            preparedStatement = connection.prepareStatement(sql);
            // 设置传入参数
            preparedStatement.setString(1, "zhangsan");
            // 执行SQL语句
            resultSet = preparedStatement.executeQuery();
            // 处理查询结果(将查询结果转换成List<Map>格式)
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();

            while (resultSet.next()) {
                Map map = new HashMap();
                for (int i = 0; i < columnCount; i++) {
                    String columnName = metaData.getColumnName(i + 1);
                    map.put(columnName, resultSet.getString(columnName));
                }
                resultList.add(map);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                // 关闭结果集
                if (resultSet != null) {
                    resultSet.close();
                    resultSet = null;
                }
                // 关闭执行
                if (preparedStatement != null) {
                    preparedStatement.close();
                    preparedStatement = null;
                }
                if (connection != null) {
                    connection.close();
                    connection = null;
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return resultList;
    }
}

3、mybatis

mybatis的基本构成

1)、SqlSessionFactoryBuilder(构造器):根据配置信息或代码来生成SqlSessionFactory
2)、SqlSessionFactory:依靠工厂来生成SqlSession(会话)
3)、SqlSession:发送SQL去执行并返回结果,也可以获取mapper的接口(Executor 才是真正执行sql)。
4)、 Mapper:它是由一个Java接口和XML文件(注解)构成的,需要给出对应的SQL和映射规则。它负责发送sql去执行,并返回结果。
mybatis的基本构成的生命周期:
1)、SqlSessionFactoryBuilder:在方法内部有效,只用于生成SqlSessionFactory
2)、SqlSessionFactory:用于生成SqlSession(会话)整个mybatis应用生命周期(单例)
3)、SqlSession:相当于一个Connection,一个请求数据库的
4)、Mapper:是一个接口,发送sql返回需要的值,最大生命跟SqlSession范围一样大。

(1)、导jar包

		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
		    <groupId>org.mybatis</groupId>
		    <artifactId>mybatis</artifactId>
		    <version>3.5.4</version>
		</dependency>

(2)、测试和应用

mysql表语句

/*
Navicat MySQL Data Transfer

Source Server         : 127.0.0.1
Source Server Version : 50640
Source Host           : localhost:3306
Source Database       : test

Target Server Type    : MYSQL
Target Server Version : 50640
File Encoding         : 65001

Date: 2020-06-03 00:21:16
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(32) NOT NULL AUTO_INCREMENT,
  `userName` varchar(32) NOT NULL,
  `passWord` varchar(50) NOT NULL,
  `realName` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'aa', '11', '11');

实体类

import java.io.Serializable;
import java.sql.Date;

public class User implements Serializable {
    private long id;
    private String userName;
    private String passWord;
    private Date createTime;
    private String name;

    public long getId() {
        return id;
    }

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                ", passWord='" + passWord + '\'' +
                ", createTime=" + createTime +
                ", name='" + name + '\'' +
                '}';
    }
}

XML文件构建SqlSessionFactory实现CURD

每个MyBatis应用程序都以SqlSessionFactory实例为中心。可以使用SqlSessionFactoryBuilder获取SqlSessionFactory实例。SqlSessionFactoryBuilder可以从XML配置文件或Configuration类的自定义准备好的实例中构建SqlSessionFactory实例。

从XML文件构建SqlSessionFactory实例非常简单。建议您为该配置使用类路径资源,但可以使用任何InputStream实例,包括从文字文件路径或file:// URL创建的实例。MyBatis包含一个称为资源的实用程序类,该实用程序类包含许多方法,这些方法使从类路径和其他位置加载资源更加容易。

mapper文件

import pojo.User;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserMapper {
    List<User> selectUserList();
}
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new 					SqlSessionFactoryBuilder().build(inputStream);

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">
<configuration>
<properties resource="db.properties"></properties>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"></mapper>
    </mappers>
</configuration>

基于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="dao.UserMapper">
    <select id="selectUserList" resultType="pojo.User">
        select * from user
    </select>
</mapper>

SqlSession调用的基于XML的映射语句

        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.selectUserList();
        for (User user : users) {
            System.out.println(user);
        }

不使用XML的情况下构建SqlSessionFactory实现CURD

映射语句根本不需要与XML映射。相反,他们可以使用Java注释。

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import pojo.User;
import java.util.List;
@Mapper
public interface UserMapper {
    @Select("select * from user")
    List<User> selectUserList();
}
  public static void main(String[] args) {

        SqlSession sqlSession = null;
        try {
            sqlSession = getSqlSessionFactory().openSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> users = mapper.selectUserList();
            for (User user : users) {
                System.out.println(user);
            }

        } catch (Exception e) {
            System.out.println("执行失败");
        } finally {
            if (sqlSession != null) {
                sqlSession.close();
            }
        }

    }


 private static SqlSessionFactory getSqlSessionFactory() {
        //创建线程池
        PooledDataSource dataSource = new PooledDataSource();
        dataSource.setDriver("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        //构建数据库事物
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        //构建数据库✅环境
        Environment evironment = new Environment("development", transactionFactory, dataSource);
        //构建Configuration对象
        Configuration configuration = new Configuration(evironment);
        //注册一个别名
        configuration.getTypeAliasRegistry().registerAlias("user", User.class);
        //添加映射器
        configuration.addMapper(UserMapper.class);
        /**
         * 以上是Mybatis配置信息的处理过程正常开发中主要通过xml配置的
         * 以下是创建sqlSessionFactory
         */
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
        return sqlSessionFactory;
    }

动手模拟一次Mybatis的动态代理

代码实例:

import dao.UserMapper;
import org.apache.ibatis.annotations.Select;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.HashMap;

public class ProxyMapperTest {
    public static void main(String[] args) {
        UserMapper userMapper = (UserMapper) Proxy.newProxyInstance(ProxyMapperTest.class.getClassLoader(), new Class<?>[]{UserMapper.class}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("被动态代理类回调执行, 代理类 proxyClass =" + proxy.getClass()
                        + " 方法名: " + method.getName() + "方法. 方法返回类型:" + method.getReturnType()
                        + " 接口方法入参数组: " + (args == null ? "null" : Arrays.toString(args)));

                Select annotation = method.getAnnotation(Select.class);
                //  赋值到#
                Parameter[] parameters = method.getParameters();

                HashMap map = getParametersMap(parameters, args);

                if (annotation != null) {
                    String[] value = annotation.value();
                    String sql = value[0];
                    String sqlStr = getSql(sql, map);
                    System.out.println(sqlStr);
                }

                return null;
            }
        });
        userMapper.selectUserById(1);
    }

    private static String getSql(String sql, HashMap map) {
        StringBuilder sb = new StringBuilder();
        int length = sql.length();
        for (int i = 0; i < length; i++) {
            char a = sql.charAt(i);
            if (a == '#') {
                int nextIndex = i + 1;
                if ('{' != sql.charAt(nextIndex)) {
                    throw new RuntimeException(String.format("这里应该为{%s,index%s", sb.toString(), nextIndex));
                }
                StringBuilder sbs = new StringBuilder();
                i = parSqlarg(sbs, sql, nextIndex);
                Object argValue = map.get(sbs.toString());
                sb.append(argValue);
                continue;
            }
            sb.append(a);
        }
        return sb.toString();
    }

    private static int parSqlarg(StringBuilder sbs, String sql, int nextIndex) {
        nextIndex++;
        for (int i = nextIndex; i < sql.length(); i++) {
            char b = sql.charAt(nextIndex++);
            if ('}' != b) {
                sbs.append(b);
                continue;
            }
            if ('}' == b) {
                return nextIndex;
            }
        }
        throw new RuntimeException(String.format("这里应该为}%s,index%s", sbs.toString(), nextIndex));
    }

    private static HashMap getParametersMap(Parameter[] parameters, Object[] args) {
        HashMap map = new HashMap();
        int index[] = {0};
        Arrays.asList(parameters).forEach(parameter -> {
            String name = parameter.getName();
            map.put(name, args[index[0]]);
            index[0]++;
        });
        System.out.println(Arrays.toString(parameters));
        return map;
    }

}

jdk1.8提供了获取参数名的方法
但是在编译的时候要加上–parameters参数,如果不加这个参数会得到参数名为arg0…

Parameter[] parameters = methods.getParameters();
System.out.println(parameters[0].getName());
但是如果使用maven,只需要在编译插件上加上一个配置

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <compilerArgument>-parameters</compilerArgument>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

果使用IDEA打开javac设置
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MyBatis 是一个开源的持久层框架,可以方便地将 SQL 语句和 Java 对象进行映射。如果您想要学习 MyBatis 源码,可以按照以下步骤进行: 1. 了解 MyBatis 的架构和设计原理。可以阅读官方文档和相关书籍,例如《MyBatis 技术内幕》。 2. 下载 MyBatis 的源代码,并导入到 IDE 中。MyBatis 使用 Maven 进行构建,您可以使用 IDE 的 Maven 插件来下载依赖项。 3. 查看 MyBatis 的源代码结构。MyBatis 的主要代码在 `mybatis-3` 模块中,包括 `src/main/java` 和 `src/main/resources` 目录。其中,`src/main/java` 目录包含了 MyBatis 的核心代码,例如 `org.apache.ibatis.session.SqlSession` 类;`src/main/resources` 目录包含了 MyBatis 的配置文件和映射文件。 4. 阅读 MyBatis 的源代码。可以从 MyBatis 的入口处 `org.apache.ibatis.session.SqlSessionFactoryBuilder` 开始,深入了解 MyBatis 的初始化流程、SQL 语句的执行流程、映射文件的解析和缓存等。 5. 调试 MyBatis 的源代码。可以使用 IDE 的调试功能,对 MyBatis 进行单步调试,观察代码的执行流程,加深对 MyBatis 的理解。 6. 学习 MyBatis 的单元测试。MyBatis 的单元测试位于 `src/test/java` 目录中,可以通过单元测试来了解 MyBatis 的各个功能点的使用方法和测试用例。 7. 参与 MyBatis 的开发。如果您对 MyBatis 源码深入的了解,并希望为 MyBatis 做出贡献,可以参与 MyBatis 的开发,贡献代码和文档,提交 issue 和 PR。MyBatis 的开发社区非常活跃,可以在官方网站和 GitHub 上找到相关信息。 希望这些步骤对您学习 MyBatis 源码有所帮助。 ### 回答2: MyBatis是一个开源的Java持久层框架,通过操作对象与数据库关系映射来提供数据持久化的功能。了解MyBatis源码学习和使用该框架的重要一步。 首先,MyBatis源码结构比较清晰,主要分为核心模块和附属模块。核心模块包括XML配置解析、SQL语句解析、参数处理、数据库连接管理等功能的实现,是实现MyBatis基本功能的核心部分。附属模块包括缓存、事务、插件等额外功能的实现,可以根据需要进行扩展和配置。 学习MyBatis源码可以从以下几个方面入手: 1. 配置文件解析:MyBatis通过XML配置文件来进行相关的配置,了解配置文件的解析过程可以帮助理解MyBatis的初始化过程和各项配置的作用。 2. SQL语句解析与执行:MyBatis将SQL语句封装成MappedStatement对象进行管理,了解MappedStatement的生成过程,以及SQL语句的解析、参数处理和执行过程,可以深入了解MyBatis的SQL执行原理。 3. 会话管理和事务处理:MyBatis采用SqlSessionFactory和SqlSession来管理数据库连接和事务,在MyBatis源码中可以学习到如何管理数据库连接池、事务的提交和回滚等核心功能的实现。 4. 缓存机制:MyBatis提供了一级缓存和二级缓存的功能,了解缓存的生成和更新过程,以及缓存的命中和失效原理,可以提高数据库查询性能。 总之,通过学习MyBatis源码,可以加深对该框架的理解,掌握其内部实现原理,有助于在使用时更加灵活和高效地进行开发。同时,也为以后解决一些特殊问题提供了更多的思路和方法。 ### 回答3: MyBatis是一个优秀的持久层框架,学习源码有助于理解其底层原理和设计思想。 首先,可以从MyBatis的入口开始学习,即SqlSessionFactoryBuilder类。该类负责解析配置文件、创建Configuration对象,并通过Configuration对象创建SqlSessionFactory实例。 接下来,可以学习Configuration类,该类负责管理整个MyBatis的配置信息。其中包括了数据库连接信息、映射文件信息、缓存信息等。在该类内部,会调用XMLMapperBuilder类解析映射文件,在解析映射文件过程中,会创建MappedStatement对象,该对象表示一条SQL语句的映射信息。 学习MappedStatement对象可以了解MyBatis的SQL语句解析过程。该对象包含了SQL语句的相关信息,包括参数映射关系、返回结果映射关系等。在执行SQL语句时,会使用ParameterHandler类处理参数,通过ResultSetHandler类处理查询结果。 同时,学习到Executor接口及其实现类,可以了解MyBatis的执行过程。Executor负责执行SQL语句,其中包括了写操作的update方法和读操作的query方法。在执行过程中,会通过StatementHandler类创建PreparedStatement对象,并通过ResultSetHandler类处理执行结果。 最后,还可以学习MyBatis的事务处理和缓存机制。Transaction接口及其实现类负责事务管理,通过JDBC的事务机制实现了事务的提交和回滚。而Cache接口及其实现类负责缓存查询结果,在查询时会先从缓存中查找结果。 总结来说,通过学习MyBatis源码可以深入理解其底层原理和设计思想。从SqlSessionFactory的创建开始,到Configuration的配置解析、MappedStatement的创建,再到Executor的执行过程和Transaction的事务管理,以及Cache的缓存机制,逐步掌握MyBatis的各个组件和它们之间的交互关系。这对于我们使用MyBatis开发项目,解决问题和优化性能都具有积极的意义。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值