本文主要介绍原始jdbc操作有哪些弊端等方面说明持久层框架出现的原因,另一方面介绍如何自己仿写一个mybatis框架。学习完本文相信你对日常使用的mybatis框架有更深的理解!
简介
说到持久层框架,我们很快能想到hibernate、mybatis。Hibernate是全自动的持久层框架,而mybatis则是半自动的。那么两者有何区别?下面先简单认识一下这两个框架的异同,以便更好的理解下面阐述的自定义持久层框架的设计思想,毕竟下面的自定义框架实际就是通过阅读mybatis源码,取其核心设计,去掉细枝旁叶而设计的一个半自动持久层框架。
ORM全称Object/Relation Mapping:表示对象-关系映射的缩写。
Hibernate:全自动的持久层ORM框架。Hibernate是一个开源的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO(Plain Ordinary Java Object)与数据库表建立映射关系,是一个全自动的ORM框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。
Mybatis:MyBatis 是一款优秀的半自动持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO为数据库中的记录。
使用 Hibernate 查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取,所以它是全自动的。而 MyBatis 在查询关联对象或关联集合对象时,需要手动编写 sql 来完成,所以,称之为半自动 ORM 映射工具。
既然无论是全自动的hibernate,或是半自动的mybatis,其本质都是对JDBC进行了封装,使Java程序员可以使用对象编程思维来操纵数据库。
下面我们不妨看看使用JDBC编程是怎样的,直接使用JDBC编程会有什么问题,找到问题,解决问题,然后提出针对问题的解决方案。
jdbc操作存在的问题
一个使用原始jdbc对数据进行查询的案例
package com.kmning.wallet.jdbc;
import com.kmning.wallet.jdbc.pojo.User;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author kangming.ning
* @date 2021/5/2 9:16
*/
public class JdbcQueryDemo {
public static void main(String[] args) {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet =null;
try {
//加载数据库驱动
//Class.forName("com.mysql.jdbc.Driver");
//过驱动管理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/yourdb?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "root");
//编写sql语句
String sql="select * from user where username=? or telphone=?";
//获取预处理statement
preparedStatement = connection.prepareStatement(sql);
//设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值
preparedStatement.setString(1,"aa");
preparedStatement.setString(2,"666");
//向数据库发出sql执行查询,查询出结果集
resultSet = preparedStatement.executeQuery();
//遍历结果集,封装数据
List<User> userList=new ArrayList<>(10);
while (resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String telphone = resultSet.getString("telphone");
//封装结果集
User user = new User();
user.setId(id);
user.setUsername(username);
user.setTelphone(telphone);
userList.add(user);
}
System.out.println(userList);
}catch (Exception e){
e.printStackTrace();
}finally {
//释放资源
if (resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection!=null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
代码非常简单且常规,JDBC操作无非都类似于上面的套路。通过驱动获取数据库连接,通过PreparedStatement预编译SQL语句,设置参数,然后向数据库发出sql执行查询,查询出结果集,遍历结果集,将结果集封装到POJO对象集合。当然,最后还得将相关资源释放。
但在实际的企业应用中,数据库的表少则几十个,多则几百个,如果使用类似于上述的方式去操作数据库,不仅前期开发工作巨大,而且后期的维护也将是一场灾难。那么上述代码存在哪些问题?让我们带着疑问对代码进行分析,然后找出问题所在,然后找到解决方案去解决它们。
以上代码存在的问题:
-
数据库配置信息存在硬编码
-
数据库连接创建、释放频繁造成系统资源浪费,从而影响系统性能
-
Sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变化的可能较大,sql变动需要改变java代码
-
使用preparedStatement向占位符号传参数存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护
-
对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库记录封装成pojo对象解析比较方便
解决思路:
-
数据库配置信息提供配置文件,避免改动连接等配置信息需要重新编译代码
-
使用数据库连接池初始化连接资源
-
将sql语句、设置参数、获取结果集参数抽取到xml配置文件中
-
使用反射、内省等底层技术,自动将实体与表进行属性与字段的自动映射
问题找到了,也整理出了解决思路,那么就可以着手设计框架了。
自定义mybatis框架
我们整理出了解决思路,接下来需要编写一个通用的解决方案,也就是框架,去解决问题。下面我们整理一下实现这样一个框架需要去做哪些工作。
首先是自定义框架肯定是独立的一个jar,然后提供给客户端使用。我们可以将开发工作分为两部分,一是框架需要做哪些东西,二是使用端(项目)需要做哪些东西。为了描述与理解方便,下面称框架端为mybatis-custom框架。
mybatis-custom框架负责对jdbc进行封装,提供基本操作接口,并对结果集和pojo实体进行映射,而客户端(项目)则需要提供数据库配置信息、sql配置信息(如UserMapper.xml)。使用端提供xxMapper.xml,意味着对sql的编写的控制权还是属于用户的(半自动映射框架)。
使用端
提供两部分配置信息:一是数据库配置信息。二是sql mapper配置信息,mapper文件定义了sql语句、参数类型、返回值类型、结果返回类型等信息。
使用配置文件来提供这两部分配置信息
-
sqlMapConfig.xml:存放数据库配置信息、同时存放mapper.xml的全路径(这样在方便在解析sqlMapConfig时直接解析mapper.xml,相关信息保存到Configuration)。样例如下
<configuration> <!--数据库配置信息--> <dataSource> <property name="driverClass" value="com.mysql.jdbc.Driver"></property> <property name="jdbcUrl" value="jdbc:mysql:///yourdb?serverTimezone=Asia/Shanghai"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </dataSource> <!--存放mapper.xml的全路径--> <mapper resource="UserMapper.xml"></mapper> <mapper resource="ProductMapper.xml"></mapper> </configuration>
-
mapper.xml:存放sql配置信息。样例如下
<mapper namespace="com.kmning.mybatis.test.dao.UserDao"> <!--sql的唯一标识:namespace.id来组成 : statementId--> <select id="findAll" resultType="com.kmning.mybatis.test.pojo.User" > select * from user </select> <select id="findByCondition" resultType="com.kmning.mybatis.test.pojo.User" parameterType="com.kmning.mybatis.test.pojo.User"> select * from user where id = #{id} and username = #{username} </select> <update id="updateUserById" resultType="java.lang.Integer" parameterType="com.kmning.mybatis.test.pojo.User"> UPDATE `user` SET username=#{username},telphone=#{telphone} WHERE id=#{id}; </update> <insert id="insertUser" resultType="java.lang.Integer" parameterType="com.kmning.mybatis.test.pojo.User"> INSERT INTO `user` VALUES(#{id},#{username},#{telphone}); </insert> <delete id="deleteUserById" resultType="java.lang.Integer" parameterType="com.kmning.mybatis.test.pojo.User"> DELETE FROM `user` WHERE id=#{id}; </delete> </mapper>
自定义持久层框架端
创建框架工程,引入相关maven依赖,根据上面的分析,框架需要解析xml,用连接池解决连接创建关闭频繁问题,那么引入相关依赖如下
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>mysql
标签:自定义,框架,数据库,user,sql,mybatis,手写,id
From: https://blog.csdn.net/u012882823/article/details/139614342