目录
源码地址(已开源):https://gitee.com/sizhaohe/mini-mybatis.git 跟着源码及下述UML图来理解上手会更快,拒绝浮躁,沉下心来搞
ORM:Object Relational Mapping --> 对象关系映射,是一种程序设计技术,用于实现面向对象编程语言里面不同类型系统的数据之间的转换
记不记得刚开始学JAVA时,编写一大串JDBC相关代码来进行与数据库的交互,日后我们接触到的MyBatis、MyBatisPlus等都是使用ORM组件来实现的框架。
本篇文章提炼出mybatis【最】经典、【最】精简、【最】核心的代码设计,来实现一个【mini-mybatis】,从而熟悉并掌握ORM框架的涉及实现。
很重要,建议code前跟我一样,先将类UML图整理出来,整个类的依赖关系及代码执行流程会一目而然。
篇幅有限,展开观看
1.定义sqlsession接口
对数据库的定义和处理,本篇我们只封装一个 T selectOne(Object param);
- public interface SqlSession {
-
-
T selectOne(String statement, Object parameter); -
- void close();
- }
2.DefaultSqlSession(SqlSession的实现)
使用rt.jar包下(java.lang.sql包下)
Connection接口(负责与数据库进行连接)及PreparedStatement(执行具体sql)接口来实现
- public class DefaultSqlSession implements SqlSession{
-
- private Connection connection;
-
- private Map
mapperElement; -
- public DefaultSqlSession(Connection connection, Map
mapperElement) { - this.connection = connection;
- this.mapperElement = mapperElement;
- }
-
- @Override
- public
T selectOne(String statement, Object parameter) { - XNode xNode = mapperElement.get(statement);
- Map
parameterMap = xNode.getParameter(); - try {
- PreparedStatement preparedStatement = connection.prepareStatement(xNode.getSql());
- buildParameter(preparedStatement, parameter, parameterMap);
- // SQL执行结果集的行数据
- ResultSet resultSet = preparedStatement.executeQuery();
- List
objects = resultSet2Obj(resultSet, Class.forName(xNode.getResultType())); - return objects.get(0);
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
-
- private
List resultSet2Obj(ResultSet resultSet, Class> clazz) { - List
list = new ArrayList<>(); - try {
- ResultSetMetaData metaData = resultSet.getMetaData();
- int columnCount = metaData.getColumnCount();
- // 每次遍历行值
- while (resultSet.next()) {
- T obj = (T) clazz.newInstance();
- for (int i = 1; i <= columnCount; i++) {
- Object value = resultSet.getObject(i);
- String columnName = metaData.getColumnName(i);
- String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
- Method method;
- if (value instanceof Timestamp) {
- method = clazz.getMethod(setMethod, Date.class);
- } else {
- method = clazz.getMethod(setMethod, value.getClass());
- }
- method.invoke(obj, value);
- }
- list.add(obj);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return list;
- }
-
-
- @Override
- public void close() {
- if (null == connection) return;
- try {
- connection.close();
- } catch (SQLException e) {
- e.printStackTrace();
- }
- }
-
- private void buildParameter(PreparedStatement preparedStatement, Object parameter, Map
parameterMap) throws SQLException, IllegalAccessException { -
- int size = parameterMap.size();
- // 单个参数
- if (parameter instanceof Long) {
- for (int i = 1; i <= size; i++) {
- preparedStatement.setLong(i, Long.parseLong(parameter.toString()));
- }
- return;
- }else{
- // TODO 后面紧跟的章节继续补充其他类型的入参
- }
-
- }
- }
3.定义SqlSessionFactory接口
每次执行一个SQL语句,应用程序都需要获取一个SqlSession对象。SqlSession对象是执行持久化操作的入口点,可以用于执行SQL语句、刷新缓存、提交事务等操作。建议在使用完SqlSession后,及时关闭它来释放资源。
- public interface SqlSessionFactory {
-
- SqlSession openSession();
- }
4.DefaultSqlSessionFactory(上述接口实现类)
构造方法中向下传递了Configuration配置文件
- public class DefaultSqlSessionFactory implements SqlSessionFactory {
-
- private final Configuration configuration;
-
- public DefaultSqlSessionFactory(Configuration configuration) {
- this.configuration = configuration;
- }
-
- @Override
- public SqlSession openSession() {
- return new DefaultSqlSession(configuration.getConnection(), configuration.getMapperElement());
- }
- }
5.SqlSessionFactoryBuilder
数据库操作的核心类,负责解析Mapper文件(拿datasource,数据库连接信息,mapper文件中sql的各个信息如id,入返参类型,sql)
- public class SqlSessionFactoryBuilder {
-
- public DefaultSqlSessionFactory build(Reader reader) {
- SAXReader saxReader = new SAXReader();
- Document document = null;
- try {
- document = saxReader.read(new InputSource(reader));
- // 拿到根标签元素
- Element rootElement = document.getRootElement();
- Configuration configuration = parseConfiguration(rootElement);
- return new DefaultSqlSessionFactory(configuration);
- } catch (DocumentException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- public Configuration parseConfiguration(Element rootElement) {
- Configuration configuration = new Configuration();
- configuration.setDataSource(dataSource(rootElement.selectNodes("//dataSource")));
- configuration.setConnection(connection(configuration.getDataSource()));
- configuration.setMapperElement(mapperElement(rootElement.selectNodes("//mappers")));
- return configuration;
- }
-
- private Map
dataSource(List list) { - Map
dataSource = new HashMap<>(4); - Element element = list.get(0);
- List content = element.content();
- for (Object o : content) {
- Element e = (Element) o;
- String name = e.attributeValue("name");
- String value = e.attributeValue("value");
- dataSource.put(name, value);
- }
- return dataSource;
- }
-
- private Connection connection(Map
dataSource) { - try {
- return DriverManager.getConnection(dataSource.get("url"), dataSource.get("username"), dataSource.get("password"));
- } catch (SQLException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- private Map
mapperElement(List list) { - Map
map = new HashMap<>(); - Element element = list.get(0);
- List content = element.content();
- try {
- for (Object o : content) {
- Element e = (Element) o;
- // 拿到mapper文件对应地址
- String resource = e.attributeValue("resource");
- Reader reader = Resources.getResourceAsReader(resource);
- SAXReader saxReader = new SAXReader();
- Document document = saxReader.read(new InputSource(reader));
- Element rootElement = document.getRootElement();
- String namespace = rootElement.attributeValue("namespace");
- List
selectNodes = rootElement.selectNodes("select"); - for (Element ele : selectNodes) {
- String id = ele.attributeValue("id");
- String parameterType = ele.attributeValue("parameterType");
- String resultType = ele.attributeValue("resultType");
- String sql = ele.getText();
- // ? 匹配
- Map
parameter = new HashMap<>(); - Pattern pattern = Pattern.compile("(#\\{(.*?)})");
- Matcher matcher = pattern.matcher(sql);
- for (int i = 1; matcher.find(); i++) {
- String g1 = matcher.group(1);
- String g2 = matcher.group(2);
- parameter.put(i, g2);
- sql = sql.replace(g1, "?");
- }
- XNode xNode = new XNode();
- xNode.setId(id);
- xNode.setNameSpace(namespace);
- xNode.setParameterType(parameterType);
- xNode.setResultType(resultType);
- xNode.setSql(sql);
- xNode.setParameter(parameter);
- map.put(namespace + "." + id, xNode);
- }
- }
- }catch (Exception e){
- e.printStackTrace();
- }
- return map;
- }
- }
建表
- DROP TABLE IF EXISTS `user`;
- CREATE TABLE `user` (
- `id` bigint(20) NOT NULL COMMENT '自增id',
- `userId` varchar(9) DEFAULT NULL COMMENT '用户ID',
- `userNickName` varchar(32) DEFAULT NULL COMMENT '用户昵称',
- `userHead` varchar(255) DEFAULT NULL COMMENT '用户头像',
- `userPassword` varchar(255) DEFAULT NULL COMMENT '用户密码',
- `createTime` datetime DEFAULT NULL COMMENT '创建时间',
- `updateTime` datetime NOT NULL COMMENT '更新时间',
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
- -- ----------------------------
- -- Records of user
- -- ----------------------------
- BEGIN;
- INSERT INTO `user` VALUES (1, '001', 'xxx', '001', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58');
- INSERT INTO `user` VALUES (2, '002', 'xxx2', '002', '123', '2023-07-14 17:33:55', '2023-07-14 17:33:58');
- COMMIT;
-
- SET FOREIGN_KEY_CHECKS = 1;
定义POJO及DAO
- @Data
- public class User {
-
- private Long id;
- private String userId; // 用户ID
- private String userNickName; // 昵称
- private String userHead; // 头像
- private String userPassword; // 密码
- private Date createTime; // 创建时间
- private Date updateTime; // 更新时间
- }
- public interface IUserDao {
- User queryUserInfoById(Long id);
- }
ORM配置文件--mybatis-config-datasource.xml
- "1.0" encoding="UTF-8"?>
- "-//mybatis.org//DTD Config 3.0//EN"
- "http://mybatis.org/dtd/mybatis-3-config.dtd">
-
-
default="development"> -
"development"> -
"JDBC"/> -
"POOLED"> -
"driver" value="com.mysql.jdbc.Driver"/> -
"url" value="jdbc:mysql://172.17.1.245:3306/airticketbasedb?useUnicode=true"/> -
"username" value="write"/> -
"password" value="write123"/> -
-
-
-
-
-
"mapper/User_Mapper.xml"/> -
-
Mapper配置
UserMapper.xml
- "1.0" encoding="UTF-8"?>
- "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
"com.example.minimybatis.dao.IUserDao"> -
-
- SELECT id, userId, userNickName, userHead, userPassword, createTime
- FROM user
- where id = #{id}
-
-
测试类
- public class ApiTest {
- @Test
- public void test(){
- String resouce = "mybatis-config-datasource.xml";
- Reader reader;
- try{
- reader = Resources.getResourceAsReader(resouce);
- SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
- SqlSession sqlSession = sqlSessionFactory.openSession();
- User user = sqlSession.selectOne(
- "com.example.minimybatis.dao.IUserDao.queryUserInfoById",
- 1L);
- System.out.println(JSONObject.toJSONString(user));
- }catch (Exception e){
- e.printStackTrace();
- }
- }
- }
比mybatis小很多,取其(mybaits)精华来达到掌握ORM框架的目的