【不说废话】MyBatis系列(一):手写简单持久层框架

Mybatis源码详解系列(一)–持久层框架解决了什么及如何使用Mybatis 简介 Mybatis 是一个持久层框架,它对 JDBC 进行了高级封装,使我们的代码中不会出现任何的 JDBC 代码,另外,它还通过 xml 或注解的方式将 sql 从 DAO/Repository 层中解耦出来,除了这些基本功能外,它还提供了动态 sql、延迟加载、缓存等功能。 相比 Hibernate,Mybatis 更面向数据库,可以灵活地对 sql 语句进行优化。 针对 Myba... 阅读详情

不说废话,直接开始

分析jdbc操作问题

思考

jdbc已经能够完成数据库的操作,已经能完成增删该查的操作,为什么改需要 MyBatis 呢?

原因

jdbc操作存在一些问题,哪些问题?通过 jdbc 代码分析

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/mybatis?characterEncoding=utf-8", "root", "root");
        
        // 定义sql语句?表示占位符 
        String sql = "select * from user where username = ?"; 
        // 获取预处理statement 
        preparedStatement = connection.prepareStatement(sql); 
        // 设置参数,第⼀个参数为sql语句中参数的序号(从1开始),第⼆个参数为设置的参数值
        preparedStatement.setString(1, "tom"); 
        // 向数据库发出sql执⾏查询,查询出结果集 
        resultSet = preparedStatement.executeQuery(); 
        
        // 遍历查询结果集 
        while (resultSet.next()) { 
            int id = resultSet.getInt("id"); 
            String username = resultSet.getString("username"); 
            // 封装User 
            user.setId(id); 
            user.setUsername(username); 
        } 
        System.out.println(user); 
    } 
    
} 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问题分析.png

问题总结

  1. 数据库的连接创建、释放频繁造成系统资源浪费,从而影响系统性能
  2. sql语句在代码中硬编码,造成代码不易维护,实际应用中sql变换的可能较大,sql变动需要改变java代码
  3. 使用prepared Statement向占有位符号传参存在硬编码,因为sql语句的where条件不一定,可能多也可能少,修改sql还要修改代码,系统不易维护
  4. 对结果集解析存在硬编码(查询列名),sql变化导致解析代码变化,系统不易维护,如果能将数据库的库记录封装成pojo对象解析比较方便

问题解决

  1. 使用数据库连接池初始化连接资源
  2. 将sql语句抽取到xml配置文件中
  3. 使用反射、内省等底层技术,将自动实现与表进行属性与字段的自动映射

自定义持久层框架

设计思路

使用端(项目):

  • 引入自定义持久层框架的jar包
  • 使用配置文件提供两部分配置信息
    • sqlMapConfig.xml:数据库配置信息、mapper.xml的全路径
    • mapper.xml:sql配置信息:sql语句、参数类型、返回值类型

自定义持久层框架本身(工程):本质就是对JDBC代码进行封装

  1. 加载配置文件:根据配置文件的路径,加载配置文件成字节流,存储在内存中

    创建类:Resources 方法:InputSteam getResourceAsSteam(String path)

  2. 创建两个javaBean(容器对象):存放的就是对配置文件解析出来的内容

    Configuration(核心配置类):存放sqlMapConfig.xml解析出来的内容

    MappedStatement(映射配置类):存放mapper.xml解析出来的内容

  3. 解析配置文件:dom4j

    创建类:SqlSessionFactoryBuilder 方法:build(InputSteam in)

    (1) 使用dom4j解析配置文件,将解析出来的内容封装到容器对象中

    (2) 创建SqlSessionFactory对象:生产sqlSession(会话对象)— 工厂模式

  4. 创建SqlSessionFactory接口及实现类DefaultSqlSessionFactory

    openSession( ):生产sqlSession

  5. 创建SqlSession接口及实现类DefaultSession

    定义对数据库的crud操作:selectList( )、selectOne( )、update( )、delete( )

  6. 创建Executor接口及实现类SimpleExecutor实现类

    query(Configuration, MappedStatement, Object… params):执行的就是JDBC代码

图解分析

自定义mybatis分析.jpg

代码实现

创建Modules:IPersistence(自定义持久层框架)和 IPersistence_test(用户端)

代码实现中有详细注释!!

使用端

IPersistence_test

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zx</groupId>
    <artifactId>IPersistence_test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <!-- 引入自定义持久层框架依赖 -->
    <dependencies>
        <dependency>
            <groupId>com.zx</groupId>
            <artifactId>IPersistence</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
sqlMapConfig.xml

数据库配置信息、mapper.xml的全路径

<configuration>
    <!-- 数据库配置信息 -->
    <dataSource>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///zx_mybatis"></property>
        <property name="" value="root"></property>
        <property name="" value="root"></property>
    </dataSource>

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

sql配置信息:sql语句、参数类型、返回值类型

<mapper namespace="user">
    <!-- sql的唯一标识:namespace.id来组成的 -->
    <select id="selectList" resultType="com.zx.pojo.User">
        select * from user
    </select>

    <select id="selectOne" resultType="com.zx.pojo.Use" paramterType="com.zx.pojo.Use">
        select * from user where id = #{id} and username = #{username}
    </select>
</mapper>
User
package com.zx.pojo;

import java.io.Serializable;

public class User implements Serializable {
    private Integer id;
    private String username;

    // getter... setter...
}
IPersistenceTest
package com.zx.test;

import com.zx.io.Resources;
import com.zx.pojo.User;
import com.zx.sqlSession.SqlSession;
import com.zx.sqlSession.SqlSessionFactory;
import com.zx.sqlSession.SqlSessionFactoryBuilder;
import org.junit.Test;

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

public class IPersistenceTest {
    @Test
    public void test() throws Exception {
        InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = sqlSessionFactory.openSqlSession();
        // 调用
        List<User> list = sqlSession.selectList("user.selectList");
        for (User user1 : list) {
            System.out.println(user1);
        }
    }
}
自定义持久层框架

IPersistence

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.zx</groupId>
    <artifactId>IPersistence</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.17</version>
        </dependency>

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

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.12</version>
        </dependency>

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

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

        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
        </dependency>
    </dependencies>
</project>
Configuration
package com.zx.pojo;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * Configuration(核心配置类):存放sqlMapConfig.xml解析出来的内容
 * Configuration 即包含了数据库的配置信息:dataSource
 *               也包含了sql的配置信息:mappedStatementMap
 */
public class Configuration {
    /**
     * 存放数据源配置
     */
    private DataSource dataSource;
    /**
     * key:statementId
     * value:封装好的 MappedStatement 对象
     */
    Map<String, MappedStatement> mappedStatementMap = new HashMap<>();

    // getter... setter...
}
MappedStatement
package com.zx.pojo;

/**
 * MappedStatement(映射配置类):存放mapper.xml解析出来的内容
 */
public class MappedStatement {
    /**
     * id标识
     */
    private String id;
    /**
     * 返回值类型
     */
    private String resultType;
    /**
     * 参数值类型
     */
    private String parameterType;
    /**
     * sql语句
     */
    private String sql;

    // getter... setter...
}
Resources
package com.zx.io;

import java.io.InputStream;

public class Resources {
    /**
     * 根据配置文件的路径,将配置文件加载成字节输入流,存储在内存中
     * @param path:使用端传过来的配置文件的路径
     * @return resourceAsStream:字节输入流
     */
    public static InputStream getResourceAsStream(String path) {
        // 获取类加载器
        InputStream resourceAsStream = Resources.class.getClassLoader().getResourceAsStream(path);
        return resourceAsStream;
    }
}
SqlSessionFactoryBuilder
package com.zx.sqlSession;

import com.zx.config.XMLConfigBuilder;
import com.zx.pojo.Configuration;
import org.dom4j.DocumentException;

import java.beans.PropertyVetoException;
import java.io.InputStream;

/**
 * 作用:
 *  1.使用dom4j解析配置文件,将解析出来的内容封装到容器对象中
 *  2.创建SqlSessionFactory对象,来生产sqlSession(会话对象)
 */
public class SqlSessionFactoryBuilder {
    public SqlSessionFactory build(InputStream in) throws DocumentException, PropertyVetoException {
        // 1.使用dom4j解析配置文件,将解析出来的内容封装到Configuration中
        XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder();
        Configuration configuration = xmlConfigBuilder.parseConfig(in);
        // 2.创建sqlSessionFactory对象:工厂类:生产sqlSession:会话对象
        DefaultSqlSessionFactory defaultSqlSessionFactory = new DefaultSqlSessionFactory(configuration);
        return defaultSqlSessionFactory;
    }
}
XMLConfigBuilder
package com.zx.config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.zx.io.Resources;
import com.zx.pojo.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.beans.PropertyVetoException;
import java.io.InputStream;
import java.util.List;
import java.util.Properties;

public class XMLConfigBuilder {

    private Configuration configuration;
    public XMLConfigBuilder() {
        this.configuration = new Configuration();
    }
    /**
     * 该方法就是使用dom4j对配置文件解析,封装Configuration
     * @param inputStream
     * @return
     */
    public Configuration parseConfig(InputStream inputStream) throws DocumentException, PropertyVetoException {
        /*
         * 对 sqlMapConfig.xml 进行解析,封装Configuration
         */
        Document document = new SAXReader().read(inputStream);
        // 拿到sqlMapConfig.xml里的<configuration>
        Element rootElement = document.getRootElement();
        // 查找configuration里面的元素,"//xxx"-表示xxx在配置文件的任意位置都能够查找到
        List<Element> list = rootElement.selectNodes("//property");
        Properties properties = new Properties();
        for (Element element : list) {
            // 获取property标签里的 name 的值
            String name = element.attributeValue("name");
            // 获取property标签里的 value 的值
            String value = element.attributeValue("value");
            // name 和 value 的值都封装到 properties 里
            properties.setProperty(name, value);
        }
        // 使用c3p0连接池,创建连接池对象,将他们封装到configuration这个对象中
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
        comboPooledDataSource.setDriverClass(properties.getProperty("driverClass"));
        comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl"));
        comboPooledDataSource.setUser(properties.getProperty("username"));
        comboPooledDataSource.setPassword(properties.getProperty("password"));
        configuration.setDataSource(comboPooledDataSource);
        /*
         * 对 mapper.xml 进行解析,封装Configuration
         * 拿到路径 -- 字节输入流 -- dom4j进行解析
         */
        List<Element> mapList = rootElement.selectNodes("//mapper");
        // 每一个element就是对应的sqlMapConfig.xml里的一个<mapper>标签
        // <mapper resource="UserMapper.xml"></mapper>
        for (Element element : mapList) {
            // 拿到路径
            String mapperPath = element.attributeValue("resource");
            // 获取字节输入流
            InputStream resourceAsStream = Resources.getResourceAsStream(mapperPath);
            // dom4j进行解析
            XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(configuration);
            xmlMapperBuilder.parse(resourceAsStream);
        }
        return configuration;
    }
}

XMLMapperBuilder
package com.zx.config;

import com.zx.pojo.Configuration;
import com.zx.pojo.MappedStatement;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

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

public class XMLMapperBuilder {

    private Configuration configuration;
    public XMLMapperBuilder(Configuration configuration) {
        this.configuration = configuration;
    }

    public void parse(InputStream inputStream) throws DocumentException {
        Document document = new SAXReader().read(inputStream);
        Element rootElement = document.getRootElement();
        String namespace = rootElement.attributeValue("namespace");
        List<Element> list = rootElement.selectNodes("//select");
        // element对应的就是mapper.xml里的select语句标签
        for (Element element : list) {
            // 获取每个标签的id,resultType,parameterType,sql语句
            String id = element.attributeValue("id");
            String resultType = element.attributeValue("resultType");
            String parameterType = element.attributeValue("parameterType");
            String sqlText = element.getTextTrim();
            // 封装到MappedStatement实体类中
            MappedStatement mappedStatement = new MappedStatement();
            mappedStatement.setId(id);
            mappedStatement.setResultType(resultType);
            mappedStatement.setParameterType(parameterType);
            mappedStatement.setSql(sqlText);
            // sql的唯一标识:statement  namespace.id
            String key = namespace +"."+ id;
            configuration.getMappedStatementMap().put(key, mappedStatement);
        }
    }
}
SqlSessionFactory
package com.zx.sqlSession;

public interface SqlSessionFactory {
    public SqlSession openSqlSession();
}
DefaultSqlSessionFactory
package com.zx.sqlSession;

import com.zx.pojo.Configuration;

public class DefaultSqlSessionFactory implements SqlSessionFactory {

    private Configuration configuration;
    public DefaultSqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public SqlSession openSqlSession() {
        return new DefaultSqlSession(configuration);
    }
}
SqlSession
package com.zx.sqlSession;

import java.util.List;

public interface SqlSession {
    /**
     * 查询所有
     * @param statementId:对应 mapper.xml 里的sql唯一标识:namespace.id,Configuration中封装的key值
     * @param params:按照条件查询 模糊查询
     * @param <E>
     * @return
     */
    public <E> List<E> selectList(String statementId, Object... params) throws Exception;
    /**
     *
     * @param statementId:对应 mapper.xml 里的sql唯一标识:namespace.id,Configuration中封装的key值
     * @param params:按照条件查询
     * @param <T>
     * @return
     */
    public <T> T selectOne(String statementId, Object... params) throws Exception;
}
DefaultSqlSession
package com.zx.sqlSession;

import com.zx.pojo.Configuration;
import com.zx.pojo.MappedStatement;

import java.util.List;

public class DefaultSqlSession implements SqlSession {

    private Configuration configuration;
    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }

    @Override
    public <E> List<E> selectList(String statementId, Object... params) throws Exception {
        // 将要去完成对simpleExecutor里的query方法的调用
        SimpleExecutor simpleExecutor = new SimpleExecutor();
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
        List<E> list = simpleExecutor.query(configuration, mappedStatement, params);
        return list;
    }

    @Override
    public <T> T selectOne(String statementId, Object... params) throws Exception {
        List<Object> objects = selectList(statementId, params);
        if (objects.size() == 1) {
            return (T) objects.get(0);
        }else {
            throw new RuntimeException("查询结果为空或返回结果过多");
        }
    }
}
Executor
package com.zx.sqlSession;

import com.zx.pojo.Configuration;
import com.zx.pojo.MappedStatement;

import java.util.List;

public interface Executor {
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception;
}
SimpleExecutor
package com.zx.sqlSession;

import com.zx.config.BoundSql;
import com.zx.pojo.Configuration;
import com.zx.pojo.MappedStatement;
import com.zx.utils.GenericTokenParser;
import com.zx.utils.ParameterMapping;
import com.zx.utils.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {

        // 1.注册驱动,获取连接
        Connection connection = configuration.getDataSource().getConnection();

        // 2.获取sql语句 select * from user where id = #{id} and username = #{username}
        //   转换sql语句 select * from user where id = ? and username = ?,转换过程中,还要对#{}里面的值进行解析存储
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);

        // 3.获取预处理对象:preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        // 4.设置参数
            // 获取参数的全路径
        String parameterType = mappedStatement.getParameterType();
            // 根据参数的全路径获取它的class对象
        Class<?> parameterTypeClass = getClassType(parameterType);

        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {
            // 取出parameterMappingList里的每一个对象
            ParameterMapping parameterMapping = parameterMappingList.get(i);
            // 取出#{}里面的内容 id,username
            String content = parameterMapping.getContent();
            // 反射:通过content获取实体类的属性值
            Field declaredField = parameterTypeClass.getDeclaredField(content);
            // 设置暴力访问
            declaredField.setAccessible(true);
            // 设置参数
            Object o = declaredField.get(params[0]);
            preparedStatement.setObject(i+1, o);
        }

        // 5.执行sql
        // 查询出来的结果封装在 resultSet 中
        ResultSet resultSet = preparedStatement.executeQuery();

        // 6.封装返回结果集
        // 获取返回值类型
        String resultType = mappedStatement.getResultType();
        // 转为class对象(new PropertyDescriptor使用)
        Class<?> resultTypeClass = getClassType(resultType);
        // 用于存放封装好的o
        ArrayList<Object> objects = new ArrayList<>();
        while (resultSet.next()) {
            // 获取resultTypeClass的具体实现,Object对象(writeMethod.invoke使用)
            Object o = resultTypeClass.newInstance();
            // 取出resultSet的元数据,元数据中包含了字段名
            ResultSetMetaData metaData = resultSet.getMetaData();
            // metaData.getColumnCount()列的个数,数据库里有两列,则小于等于2
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                // 字段名 metaData.getColumnName(i)要从1开始,所以定义为i=1开始遍历
                String columnName = metaData.getColumnName(i);
                // 字段的值
                Object value = resultSet.getObject(columnName);
                // 使用反射或者内省,根据数据库表和实体类的对应关系,完成封装
                // PropertyDescriptor类会将resultTypeClass对象中的columnName属性生成读写方法
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                // 获取propertyDescriptor的写方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                // 把字段的值value封装到对象o中
                writeMethod.invoke(o, value);
            }
            // 将封装好的o存进List集合中
            objects.add(o);
        }
        return (List<E>) objects;
    }

    private Class<?> getClassType(String parameterType) throws ClassNotFoundException {
        if (parameterType != null) {
            Class<?> aClass = Class.forName(parameterType);
            return aClass;
        }
        return null;
    }

    /**
     * 完成对#{}的解析工作
     *  1.将#{}使用?进行代替
     *  2.解析出#{}里面的值进行存储
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        // 标记处理类:配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        // 标记解析器  openToken:开始标记,closeToken:结束标记,handler:标记处理器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // parseSql就是解析后的sql语句
        String parseSql = genericTokenParser.parse(sql);
        // parameterMappings就是解析出来的#{}参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
        return boundSql;
    }
}
BoundSql
package com.zx.config;

import com.zx.utils.ParameterMapping;

import java.util.ArrayList;
import java.util.List;

public class BoundSql {
    /**
     * 解析过后的sql
     */
    private String sqlText;
    /**
     * 解析出来的参数集合
     */
    private List<ParameterMapping> parameterMappingList = new ArrayList<>();

    public BoundSql(String sqlText, List<ParameterMapping> parameterMappingList) {
        this.sqlText = sqlText;
        this.parameterMappingList = parameterMappingList;
    }

    // getter... setter...
}
GenericTokenParser

(MyBatis源码中粘贴)

package com.zx.utils;

public class GenericTokenParser {

    private final String openToken; //开始标记
    private final String closeToken; //结束标记
    private final TokenHandler handler; //标记处理器

    public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
        this.openToken = openToken;
        this.closeToken = closeToken;
        this.handler = handler;
    }

    /**
     * 解析${}和#{}
     * @param text
     * @return
     * 该方法主要实现了配置文件、脚本等片段中占位符的解析、处理工作,并返回最终需要的数据。
     * 其中,解析工作由该方法完成,处理工作是由处理器handler的handleToken()方法来实现
     */
    public String parse(String text) {
        // 验证参数问题,如果是null,就返回空字符串。
        if (text == null || text.isEmpty()) {
            return "";
        }
        // 下面继续验证是否包含开始标签,如果不包含,默认不是占位符,直接原样返回即可,否则继续执行。
        int start = text.indexOf(openToken, 0);
        if (start == -1) {
            return text;
        }
        // 把text转成字符数组src,并且定义默认偏移量offset=0、存储最终需要返回字符串的变量builder,
        // text变量中占位符对应的变量名expression。判断start是否大于-1(即text中是否存在openToken),如果存在就执行下面代码
        char[] src = text.toCharArray();
        int offset = 0;
        final StringBuilder builder = new StringBuilder();
        StringBuilder expression = null;
        while (start > -1) {
            // 判断如果开始标记前如果有转义字符,就不作为openToken进行处理,否则继续处理
            if (start > 0 && src[start - 1] == '\\') {
                builder.append(src, offset, start - offset - 1).append(openToken);
                offset = start + openToken.length();
            } else {
                //重置expression变量,避免空指针或者老数据干扰。
                if (expression == null) {
                    expression = new StringBuilder();
                } else {
                    expression.setLength(0);
                }
                builder.append(src, offset, start - offset);
                offset = start + openToken.length();
                int end = text.indexOf(closeToken, offset);
                while (end > -1) {////存在结束标记时
                    if (end > offset && src[end - 1] == '\\') {//如果结束标记前面有转义字符时
                        // this close token is escaped. remove the backslash and continue.
                        expression.append(src, offset, end - offset - 1).append(closeToken);
                        offset = end + closeToken.length();
                        end = text.indexOf(closeToken, offset);
                    } else {//不存在转义字符,即需要作为参数进行处理
                        expression.append(src, offset, end - offset);
                        offset = end + closeToken.length();
                        break;
                    }
                }
                if (end == -1) {
                    // close token was not found.
                    builder.append(src, start, src.length - start);
                    offset = src.length;
                } else {
                    //首先根据参数的key(即expression)进行参数处理,返回?作为占位符
                    builder.append(handler.handleToken(expression.toString()));
                    offset = end + closeToken.length();
                }
            }
            start = text.indexOf(openToken, offset);
        }
        if (offset < src.length) {
            builder.append(src, offset, src.length - offset);
        }
        return builder.toString();
    }
}
ParameterMapping

(MyBatis源码中粘贴)

package com.zx.utils;

public class ParameterMapping {

    private String content;

    public ParameterMapping(String content) {
        this.content = content;
    }
    // getter... setter...
}

ParameterMappingTokenHandler

(MyBatis源码中粘贴)

package com.zx.utils;

import java.util.ArrayList;
import java.util.List;

public class ParameterMappingTokenHandler implements TokenHandler {
	private List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();

	// context是参数名称 #{id} #{username}
	public String handleToken(String content) {
		parameterMappings.add(buildParameterMapping(content));
		return "?";
	}

	private ParameterMapping buildParameterMapping(String content) {
		ParameterMapping parameterMapping = new ParameterMapping(content);
		return parameterMapping;
	}
    // getter... setter...
}
TokenHandler

(MyBatis源码中粘贴)

package com.zx.utils;

public interface TokenHandler {
  String handleToken(String content);
}

自定义持久层框架优化

问题

  1. Dao层使用自定义持久层框架,存在代码重复,整个操作的过程重复(加载配置文件、创建sqlSessionFactory、生产sqlSession)
  2. statementId存在硬编码

解决方案

使用代理模式生成Dao层的代理实现类

代码实现

SqlSession中新增
/**
* 为Dao接口生成动态代理
* @param mapperClass
* @param <T>
* @return
*/
public <T> T getMapper(Class<?> mapperClass);
DefaultSqlSession中新增
@Override
public <T> T getMapper(Class<?> mapperClass) {
    // 使用JDK动态代理来为Dao接口生成动态代理对象,并返回
    Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{mapperClass}, new InvocationHandler() {
        /**
        * 底层都是去执行JDBC代码
        * 根据不同情况来调用selectList或者selectOne
        * @param proxy:当前代理对象的引用
        * @param method:当前被调用方法的引用
        * @param args:传递的参数
        * @return
        * @throws Throwable
        */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 准备参数 1:statementId:sql的唯一标识:namespace.id = 接口的全限定名.方法名
            // 方法名 findAll
            String methodName = method.getName();
            // 全限定名 com.zx.pojo.User
            String className = method.getDeclaringClass().getName();
            String statementId = className + "." + methodName;
            // 准备参数 2:params:就是args
            // 获取被调用方法的返回值类型
            Type genericReturnType = method.getGenericReturnType();
            // 判断是否进行了范型类型参数化
            if (genericReturnType instanceof ParameterizedType) {
                return selectList(statementId, args);
            }
            return selectOne(statementId, args);
        }
    });
    return (T) proxyInstance;
}

以上就是一个简单功能的MyBatis框架的自定义了

MyBatis:简化数据库操作的持久层框架 MyBatis 是一个基于 Java 的持久层框架,起源于 Apache 的 iBatis 项目,后迁移至 Google Code 并更名为 MyBatis,最终在 2013 年迁移到 GitHub。它支持自定义 SQL、存储过程和高级映射,简化了 JDBC 操作,通过 XML 或注解配置实现 Java 对象与数据库记录的映射。MyBatis 的优点包括简化数据库操作、灵活的 SQL 控制、高度可定制化、轻量级和优秀的性能,但也存在配置繁琐、SQL 维护复杂、缺乏全面 ORM 支持等缺点。与 Hiberna 阅读详情

相关推荐

Java持久层框架:MyBatis介绍

MyBatis介绍

啊Q老师 3806

xml标签的使用

Mubatis标的签 MyBatis中XML 映射文件中常见的标签 SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出): cache – 对给定命名空间的缓存配置。 cache-ref – 对其他命名空间缓存配置的引用。 resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象 parameterMap – 已被废弃!老式风格的参数映射。更好的办法是使用内联参数,此元素可能在将来被移除。 sql – 可被其他语句引用的可重用语句块。 insert – 映射插入语

Click的博客 712

MyBatis持久层框架详细解读:MyBatis快速入门篇

JavaEE 企业级 Java 项目中的经典三层架构为表现层,业务层和持久层,使用Java 代码操作数据库属于持久层内容,而 MyBatis 对 JDBC 代码进行了封装,作为一款优秀的持久层框架,专门用于简化JDBC开发。

zhangxia_的博客 2481

Mybatis持久层(一)

(1)什么是mybatis mybatis是一个持久层的ORM框架,他是对持久层技术的封装,对JDBC的封装,JDBC是访问数据库的唯一途径 (2)什么是吃就成框架 ORM框架(Object Relation Maping对象关系映射)是对JDBC进行封装,暴露给程序员的有:数据源、SQL语句、RM对象映射关系(表和类 字段和属性的对应关系) (3)开发环境的搭建 1、新建maven工程 2、...

qq_36675851的博客 681

MyBatis持久层框架的用法知识小结

MyBatis 本是apache的一个开源项目iBatis,接下来通过本文给大家介绍MyBatis持久层框架的用法知识小结,非常不错,具有参考借鉴价值,感兴趣的朋友一起学习吧

Zuken E3.series schematic/cable/Formboard 连载(1)

​ 1987-2001年,CIM-Team公司(德国)推出基于MS-DOS环境下运行的CAD软件DDS-schematic 2002年,在此基础上,推出基于windows环境下运行,应用于电气工程领域的E3.Series 2002-2006年,CIM-Team公司成为Zuken公司(日本)的一家分公司,实现电子电气系统的完整设计平台 覆盖电子、电气原理设计,三维机柜设备布局与配线设计,线缆线束设计及其各类生产报表文档生成的完整设计流程

u010930036的博客 2504

J2EE持久层框架mybatis配置文件写法

mybatis在J2EE持久层框架中,主要就是不用像以前那样不用写持久层接口的实现类了,全部用配置文件来写,配置文件的写法如下: "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> mapper namespace=""> select id="持久层接口中的方法名" resultType="自定义的映射关系"> sql语句 select>

mostlove 1679

深度残差网络的无人机多目标识别

深度残差网络的无人机多目标识别 人工智能技术与咨询 来源:《图学学报》。作者翟进有等 摘要:传统目标识别算法中,经典的区域建议网络(RPN)在提取目标候选区域时计算量大,时间复杂度较高,因此提出一种级联区域建议网络(CRPN)的搜索模式对其进行改善。此外,深层次的卷积神经网络训练中易产生退化现象,而引入残差学习的深度残差网络(ResNet),能够有效抑制该现象。对多种不同深度以及不同参数的网络模型进行研究,将两层残差学习模块与三层残差学习模块结合使用,设计出一种占用内存更小、时间复杂度更低的新型多捷.

m0_62870606的博客 3112

mybatis总结

MyBatis持久层框架使用总结 MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。 2013年11月迁移到Github,MyBatis的Github地址:https://github.com/mybatis/mybatis-...

weixin_33845477的博客 376

微信支付V2,jsapi支付拉起收银台报错:调用支付JSAPI缺少参数:total_fee

拉起收银台报错

m0_54355172的博客 3873

手把手教你用MOS管设计双电源自动切换电路(附零压降方案)

本文详细介绍了使用MOS管设计双电源自动切换电路的完整方案,包括基础电路拓扑、零压降优化设计及实战调试技巧。针对外接电源与电池供电的无缝切换需求,提供低损耗MOS管选型建议和性能实测数据,帮助工程师解决传统二极管方案的压降与漏电问题。

h0i1j2k3l的博客 1447

Mybatis(一):手写一套持久层框架

Mybatis(一):手写一套持久层框架 作者 : 潘潘 未来半年,有幸与导师们一起学习交流,趁这个机会,把所学所感记录下来。 「封面图」 自毕业以后,自己先创业后上班,浮沉了近8年,内心着实焦躁,虽一直是走科班路线,但在技术道路上却始终没静下心来研究、思考、梳理,机会来了,便抓牢。 希望自己记录下来的知识内容,对后来的学习之人,能有些许帮助。 对文章内容有任何建议或意见,或对互联网开发有希望交流学习,或单纯热爱生活,都欢迎随时微信我:panshenlian。 第一个系列的文章主要围绕「架构师

潘潘和他的朋友们 1246

运维与软件实施课表

运维与软件实施课表 课程 章节编号 章节 课程地址 软件及操作系统基础 ch01 软件及操作系统基础01 实施、运维工程师基本技能:https://blog.youkuaiyun.com/qq_28189423/article/details/82700539 老毛桃制作winPE镜像:https://blog.youkuaiyun.com/qq_2818942...

Mr.O~的博客 1万+

Mybatis学习(一):基础概念和简单自定义持久层框架demo

Mybatis学习(一):基础概念和基本应用一、一个简单自定义持久层框架demo 一、一个简单自定义持久层框架demo

RabbitInTheGrass的博客 2071

手把手教你学pcie--性能调优 - 中断机制优化

在PCIe系统开发中,中断机制是主机与设备之间通信的重要组成部分。然而,不当的中断设计可能导致CPU负载过高、延迟增加等问题。本文将详细介绍如何优化PCIe系统的中断机制,提升系统性能和响应速度。传统中断(INTx):通过专用引脚发送中断信号,可能引发冲突且效率较低。消息中断(MSI):通过内存写操作发送中断消息,避免冲突并提高效率。扩展消息中断(MSI-X):基于MSI改进,支持更多独立中断向量,适用于复杂场景。选择合适的中断模式对系统性能至关重要。

MHD0815的博客 290

(01)ORB-SLAM2源码无死角解析-(38) EPnP 算法原理详解→理论基础二:分情况求得beta初始解

讲解关于slam一系列文章汇总链接:史上最全slam从零开始,针对于本栏目讲解的(01)ORB-SLAM2源码无死角解析链接如下(本文内容来自计算机视觉life ORB-SLAM2 课程课件): (01)ORB-SLAM2源码无死角解析-(00)目录_最新无死角讲解:https://blog.youkuaiyun.com/weixin_43013761/article/details/123092196 文末正下方中心提供了本人联系方式,点击本人照片即可显示WX→官方认证{\color{blue}{文末正下方中心}

江南才尽江南山,年少无知年少狂! 1024

MyBatis持久层框架

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。insert– 映射插入语句。update– 映射更新语句。delete– 映射删除语句。select– 映射查询语句。MyBatis 在查询和结果映射做了相当多的改进。一个简单查询的 select 元素是非常简单:</

2302_78998188的博客 1037

MATLAB与图像处理(一):图像中结构张量的求法(Structure Tensor)

1.结构张量的作用:       能够区分图像中的平坦区域,边缘,角点; 2.图像中的结构张量的定义    1)是一个矩阵;    2)与图像的水平,垂直梯度有关,定义如下: 在MATLAB中,可以用如下语句求解:[Ix,Iy]=gradient(Image); 3)求出结构张量矩阵的行列式,和迹(矩阵对角线之和)   行列式:K=det(E);   迹:  

wenyusuran的专栏 5242

[由零开始] 手写Mybatis-自定义持久层框架

手写Mybatis-自定义持久层框架思路分析 JDBC存 的问题 数据库配置硬编码 频繁创建与销毁数据库连接 ,资源严重浪费 SQL语句、参数写在代码里和代码在一起,存在硬编码,不好维护, 对参数和返回结果需要手动一个一个去绑定,较为繁琐 JDBC 解决方案 使用配置文件 使用数据库连接池 使用配置文件与代码分开维护(与数据库配置文件分开),SQL配置文件可能经常变更 使用反射、内省的方式来重实现绑定与封装 自定义持久层框架分

qq497811258的博客 1186

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

简单实现Java手写Mybatis框架

Java手写Mybatis的简单实现,基于JDBC和动态代理,大量的使用了反射,实现了简单的Mybatis框架

tansenc的博客 916

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

仿Mybatis手写持久层框架

仿Mybatis手写持久层框架

及时当勉励,岁月不待人 565

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

MyBatis源码解析&手写持久层框架

JDBC API 允许应用程序访问任何形式的表格数据,特别是存储在关系数据库中的数据代码示例: 1.2 JDBC问题分析&解决思路 剖开代码,逐个分析:(1)加载驱动,获取链接:存在问题1:数据库配置信息存在硬编码问题。存在问题2:频繁创建、释放数据库连接问题。(2)定义sql、设置参数、执行查询:存在问题3:SQL语句、设置参数、获取结果集参数均存在硬编码问题 。(3)遍历查询结果集:存在问题4:手动封装返回结果集,较为繁琐针对JDBC各个环节中存在的不足,现在,整理出对应的优化思路,统一汇总:JDBC是

沉浸式Study 994

Mybatis优秀的持久层框架

1.Mybatis引入2.Mybatis的初次使用3.Mybatis的配置细节4.Mybaits完成DML全部操作(CURD)

zhzjn的博客 699

三大框架 —— 持久层框架MyBatis

三大框架 —— 持久层框架MyBatis1. mybatis介绍2. 执行流程3. 使用步骤代码实现举例:商品分类CRUD操作4. 关联关系一对一分析(图)代码实现一对多分析代码实现 1. mybatis介绍 传统框架的缺点: 使用jdbc需要程序员创建连接,手写sql,处理结果集,使用了mybatis框架后,创建连接,结果集处理都由框架来完成。 mybatis它是轻量级持久层框架,由ibatis演化而来。它自动连接数据库,将数据库的结果集封装到对象中POJO。 2. 执行流程 Mybatis启

LIKE_0217的博客 9535

【MyBatis】Java 数据持久层框架:认识 MyBatis

MyBatis 和 JPA 一样,也是一款优秀的持久层框架,它支持定制化 SQL、存储过程,以及高级映射。它可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通 Java 对象)映射成数据库中的记录。

Data · Model · Agent · Repeat 1682
评论 10
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

写BUG的小李同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值