《Java手写系列》-手写MyBatis框架

Posted IT老刘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java手写系列》-手写MyBatis框架相关的知识,希望对你有一定的参考价值。

1.前言

我们刚开始搞java的时候,貌似都知道用jdbc去连接数据库,那我们来看一下jdbc连接数据库的代码:

public static void main(String[] args) {
    Connection conn = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        // 加载数据库驱动
        Class.forName("com.mysql.jdbc.Driver");
        // 通过驱动管理类获取数据库连接
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8",
                "root", "root");
        // 定义sql语句 ?标识占位符
        String sql = "select * from user where id = ? aand username = ?";
        // 获取预处理statement
        ps = conn.prepareStatement(sql);
        // 设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为 设置的参数值
        ps.setString(1,  "riemann");
        // 向数据库发出sql执行查询,查询出结果集
        rs = ps.executeQuery();
        // 遍历结果集
        while (rs.next()) {
            int id = rs.getInt("id");
            String username = rs.getString("username");
            // 封装User
            user.setId(id);
            user.setUsername(username);
        }
        System.out.println(user);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 释放资源
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

2.JDBC问题分析

  • 数据库连接创建、释放频繁造成系统资源浪费,从而影响性能。
  • sql语句存在硬编码,造成代码不易维护。
  • 使用preparedStatement向占有位符号传参数存在硬编码问题。
  • 对结果解析存在硬编码(查询列名),sql变化导致解析代码变化。

3.问题解决方案

  • 数据库频繁创建连接以及释放资源:连接池
  • sql语句及参数存在硬编码: 配置文件
  • 手动解析封装返回结果集: 反射、内省

这也就解释了前面提的问题,为什么要有mybatis框架的存在?这是因为为了解决JDBC操作存在的这些问题。

好了,下面我们就来针对上面的解决方案来设计一个mybatis框架。

4.自定义框架实现

4.1.准备测试数据库

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `username` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'jackUpdate');
INSERT INTO `user` VALUES ('2', 'tom');

4.2.导入项目依赖

<dependencies>

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

        <!--Lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.2-jre</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>

        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>

    </dependencies>

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

4.3.创建mybatis-config.xml

<configuration>

    <!--数据库配置信息-->
    <dataSource>
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/handwrite_mybatis?useUnicode=true&amp;characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </dataSource>

    <!--存放mapper.xml的全路径-->
    <mapper resource="mappers/UserMapper.xml"></mapper>

</configuration>

4.4.创建实体类User

package com.bruce.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private Integer id;
    private String username;
}

4.5.创建Dao层

package com.bruce.dao;

import com.bruce.pojo.User;

import java.util.List;

public interface UserDao {

    /**
     * 查询所有用户
     * @return User的集合
     */
    List<User> findAll();
    
    /**
     * 根据条件进行用户查询
     * @return User对象
     * @param user
     */
    User selectOne(User user);

    void insert(User user);

    void update(User user);

    void delete(User user);
}

4.6.编写测试类

package com.bruce.test;

import com.bruce.dao.UserDao;
import com.bruce.pojo.User;
import org.junit.Before;
import org.junit.Test;

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

public class PersistenceTest {

    UserDao userDao;

    @Before
    public void before() throws Exception {
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        userDao = sqlSession.getMapper(UserDao.class);
    }

    @Test
    public void testSelect() throws Exception {
        // 调用
        List<User> all = userDao.findAll();
        System.out.println(all);
    }
}

至此为止,手写MyBatis的基础环境已经有了,但是有个问题就是测试类中的sqlSession获取的问题,因为测试类中的API很明显没有,所以接下来需要手写sqlSession代码,并且在底层需要通过动态代理的方式给接口生成实现类对象!

以上是关于《Java手写系列》-手写MyBatis框架的主要内容,如果未能解决你的问题,请参考以下文章

《Java手写系列》-手写MyBatis框架

《Java手写系列》-手写MyBatis框架

手写MyBatis,纯手工打造开源框架(第四篇:决胜千里)- 第272篇

大厂程序员,手写Mybatis

手写Mybatis,彻底搞懂框架原理

了解mybatis源码手写mybatis