MyBatis单表查询简单功能手写实现

(代码已上传,在文章顶部mybatis-salulu.zip文件)

一,mybaits的单独使用:

看mybatis官网文档,入门篇:

官网地址:https://mybatis.org/mybatis-3/zh/getting-started.html

步骤:

1,读取配置文件

2,构建sqlSession工厂

3,打开sqlSession工厂

4,获取mapper对象

5,使用mapper对象执行数据库操作

 

二,手写实现:

1,新建一个maven项目,pom.xml中引入mysql的数据库驱动即可

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

2,准备工作:测试用的表、测试类、实体类、Mapper和Mapper映射;

2.1 mysql数据库中新建一个user表,用于测试使用:

create table user
(
    id    int auto_increment
        primary key,
    name  varchar(32)    null,
    age   int default 17 null,
    email varchar(64)    null
);

表中随便写几条数据:

2.2 使用mybatis-generator-maven-plugin帮我们生成实体类、Mapper和Mapper映射;

User实体类:(省略get,set方法)

package com.salulu.test.entity;

public class User {
    private Integer id;

    private String name;

    private Integer age;

    private String email;

    
}

User的Mapper:

(对应非实体类参数,都使用了@Param注解)

package com.salulu.test.mapper;

import com.salulu.baits.annotations.Param;
import com.salulu.test.entity.User;
import java.util.List;

public interface UserMapper {
    int deleteByPrimaryKey(@Param("id") Integer id);

    int insert(User record);

    User selectByPrimaryKey(@Param("id") Integer id);

    List<User> selectByNameAndAge(@Param("name") String name,@Param("age") int age);

    List<User> selectByName(@Param("name") String name);

    List<User> selectAll();

    int updateByPrimaryKey(User record);
}

UserMapper的xml映射文件:

(做了一点修改:动态sql语句删除了,resultMap也没有使用,而是使用*来替代,因为解析起来比较麻烦)

<?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="com.salulu.test.mapper.UserMapper" >
  <resultMap id="BaseResultMap" type="com.salulu.test.entity.User" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="age" property="age" jdbcType="INTEGER" />
    <result column="email" property="email" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, name, age, email
  </sql>
  <select id="selectByPrimaryKey" resultType="com.salulu.test.entity.User" parameterType="java.lang.Integer" >
    select *
    from user
    where id = #{id,jdbcType=INTEGER}
  </select>
  <select id="selectAll" >
    select *
    from user
  </select>
  <select id="selectByNameAndAge" resultType="com.salulu.test.entity.User">
    select *
    from user
    where name = #{name,jdbcType=VARCHAR} and age = #{age,jdbcType=INTEGER}
  </select>
  <select id="selectByName" resultType="com.salulu.test.entity.User">
    select *
    from user
    where name = #{name,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
    delete from user
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="com.salulu.test.entity.User" >
    insert into user (id, name, age, 
      email)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, 
      #{email,jdbcType=VARCHAR})
  </insert>
  <update id="updateByPrimaryKey" parameterType="com.salulu.test.entity.User" >
    update user
    set name = #{name,jdbcType=VARCHAR},
      age = #{age,jdbcType=INTEGER},
      email = #{email,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

2.3 mybatis-config.xml配置文件:

(DTD校验注释了)

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=convertToNull&amp;useSSL=false&amp;allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="xiaobai"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

 

文件结构如下:

 

3,实现如下功能:

读取配置文件

构建sqlSession工厂

打开sqlSession工厂

获取mapper对象

使用mapper对象执行数据库操作

3.新建一个包,用来存放自己实现mybatis的代码

com.salulu.batis

3.1 读取配置文件

新建一个类 Resources 

package com.salulu.baits.io;

import java.io.InputStream;

public class Resources {
    // 传入资源路径,将资源读入到流中
    public static InputStream getResourceAsStream(String resource){
        InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
        return resourceAsStream;
    }
}

使用:

public static void main(String[] args) {
        // 1,读取配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
}

3.2 构建sqlSession工厂

新建一个SqlSessionFactoryBuilder类

package com.salulu.baits.session;

import com.salulu.baits.config.Configuration;
import com.salulu.baits.parse.XMLConfigBuilder;
import java.io.InputStream;

public class SqlSessionFactoryBuilder {

    public SqlSessionFactory build(InputStream inputStream){
        // 1,解析xml配置文件,得到配置封装Configuration对象
        Configuration configuration = new XMLConfigBuilder(inputStream).parse();
        //2, 根据配置文件构建sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactory(configuration);
        return sqlSessionFactory;
    }
}

3.2.1 解析xml配置文件,得到配置封装Configuration对象

package com.salulu.baits.parse;

import com.salulu.baits.config.Configuration;
import com.salulu.baits.config.Environment;
import com.salulu.baits.config.MappedStatement;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.InputStream;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

public class XMLConfigBuilder {

    private XPathParser xPathParser;

    public XMLConfigBuilder(InputStream inputStream) {
        this.xPathParser = new XPathParser(inputStream);
    }

    /**
     * 解析xml文档,把解析出来的数据封装到Configuration对象中
     * @return
     */
    public Configuration parse() {
        // xpath表达式解析xml
        Node dataSourceNode = xPathParser.xNode("/configuration/environments/environment/dataSource");
        // 数据源属性配置信息
        Properties properties = new Properties();
        NodeList propertyNodeList = dataSourceNode.getChildNodes();
        for(int i=0;i<propertyNodeList.getLength();i++){
            Node item = propertyNodeList.item(i);
            if(item.getNodeType() == Node.ELEMENT_NODE){
                properties.setProperty(item.getAttributes().getNamedItem("name").getNodeValue(),
                        item.getAttributes().getNamedItem("value").getNodeValue());
            }
        }
        // Mapper映射文件配置信息
        Map<String, MappedStatement> mappedStatementMap = new ConcurrentHashMap<>();
        Node mapperNodes = xPathParser.xNode("/configuration/mappers");
        NodeList mapperNodeList = mapperNodes.getChildNodes();
        for(int i =0;i<mapperNodeList.getLength();i++){
            Node item = mapperNodeList.item(i);
            if(item.getNodeType() == Node.ELEMENT_NODE){
                // mapper.xml文件位置
                String resource = item.getAttributes().getNamedItem("resource").getNodeValue();
                // 解析该mapper.xml文件
                InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(resource);
                this.xPathParser = new XPathParser(inputStream);
                Element element = xPathParser.getDocument().getDocumentElement();
                String namespace = element.getAttribute("namespace");
                NodeList sqlNodeList = element.getChildNodes();
                for (int j = 0; j < sqlNodeList.getLength(); j++) {
                    Node sqlNode = sqlNodeList.item(j);
                    if(sqlNode.getNodeType() == Node.ELEMENT_NODE){
                        String id = "";
                        String resultType = "";
                        String parameterType = "";
                        Node idNode = sqlNode.getAttributes().getNamedItem("id");
                        if(null == idNode) throw new RuntimeException("sql id is null");
                        else id = sqlNode.getAttributes().getNamedItem("id").getNodeValue();

                        Node resultTypeNode = sqlNode.getAttributes().getNamedItem("resultType");
                        if(null != resultTypeNode)  resultType = sqlNode.getAttributes().getNamedItem("resultType").getNodeValue();

                        Node parameterTypeNode = sqlNode.getAttributes().getNamedItem("parameterType");
                        if(null != parameterTypeNode)  parameterType = sqlNode.getAttributes().getNamedItem("parameterType").getNodeValue();

                        String sql = sqlNode.getTextContent();
                        sql = sql.replaceAll("[\r\n| ]+"," ");
                        MappedStatement mappedStatement = new MappedStatement();
                        mappedStatement.setId(id);
                        mappedStatement.setNamespace(namespace);
                        mappedStatement.setParameterType(parameterType);
                        mappedStatement.setResultType(resultType);
                        mappedStatement.setSql(sql);
                        mappedStatementMap.put(namespace+"."+id,mappedStatement);
                    }
                }
            }
        }
        //  将解析出来的值封装到Configuration
        Configuration configuration = new Configuration();
        Environment environment = new Environment();
        environment.setDriver(properties.getProperty("driver"));
        environment.setPassword(properties.getProperty("password"));
        environment.setUsername(properties.getProperty("username"));
        environment.setUrl(properties.getProperty("url"));
        configuration.setEnvironment(environment);
        configuration.setMappedStatementMap(mappedStatementMap);
        return configuration;
    }
}
XPathParser对象:
package com.salulu.baits.parse;

import com.salulu.baits.exception.BuilderException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.InputStream;

public class XPathParser {

    private XPath xPath;
    private Document document;

    public XPathParser(InputStream inputStream) {
        this.xPath = createXPath();
        this.document = createDocument(new InputSource(inputStream));
    }

    // 初始化XPath
    public XPath createXPath(){
        XPathFactory xPathFactory = XPathFactory.newInstance();
        XPath xPath = xPathFactory.newXPath();
        return xPath;
    }

    private Document createDocument(InputSource inputSource){
        try {
            // JDK 提供的文档解析工厂
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setNamespaceAware(false);// 设置是否支持命名空间
            factory.setIgnoringComments(true);// 设置是否忽略注释
            factory.setIgnoringElementContentWhitespace(false);// 设置是否忽略元素内容空白
            factory.setCoalescing(false);// 是否将CDATA节点转换为文本节点
            factory.setExpandEntityReferences(true);// 设置是否展开实体引用节点,这里是sql片段引用
            factory.setValidating(false);
            DocumentBuilder builder = factory.newDocumentBuilder();// 创建一个documentBuilder对象
            builder.setErrorHandler(new ErrorHandler() {
                @Override
                public void warning(SAXParseException e) throws SAXException {
                    throw e;
                }

                @Override
                public void error(SAXParseException e) throws SAXException {
                    throw e;
                }

                @Override
                public void fatalError(SAXParseException e) throws SAXException {
                    throw e;
                }
            });

            Document parse = builder.parse(inputSource);
            return parse;
        } catch (Exception e) {
            throw new BuilderException("创建document发生错误. cause:"+e,e);
        }
    }

    // 根据一个xpath表达式解析一个xml节点
    public Node xNode(String expression){
        Node node = null;
        try {
            node = (Node) xPath.evaluate(expression,document, XPathConstants.NODE);
        } catch (XPathExpressionException e) {
            e.printStackTrace();
        }
        return node;
    }

    public XPath getxPath() {
        return xPath;
    }

    public void setxPath(XPath xPath) {
        this.xPath = xPath;
    }

    public Document getDocument() {
        return document;
    }

    public void setDocument(Document document) {
        this.document = document;
    }


}

3.2.2 根据配置文件构建sqlSessionFactory

创建一个SqlSessionFactory类,传入配置信息(主要是需要读取到数据库连接配置)

package com.salulu.baits.session;

import com.salulu.baits.config.Configuration;
import com.salulu.baits.executor.Executor;

public class SqlSessionFactory {

    // 传入的配置信息
    private Configuration configuration;

    public SqlSessionFactory(Configuration configuration) {
        this.configuration = configuration;
    }

    public SqlSession openSession() {
        // 创建一个执行器
        Executor executor = new Executor(configuration);
        // 创建一个SqlSession对象,并传入执行器用来执行sql
        SqlSession session = new SqlSession(configuration, executor);
        return session;
    }
}

3.2.2.1 创建一个执行器Executor类

package com.salulu.baits.executor;

import com.salulu.baits.config.Configuration;
import com.salulu.baits.config.MappedStatement;
import com.salulu.baits.datasource.PooledDataSource;
import com.salulu.baits.reflection.Reflection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 执行器
 */
public class Executor {
    private Configuration configuration;
    private PooledDataSource pooledDataSource;

    public Executor(Configuration configuration) {
        this.configuration = configuration;
        // 初始化数据源,创建一个连接池
        pooledDataSource = new PooledDataSource(configuration.getEnvironment());
    }

    /**
     * 传入sql和参数执行sql语句并返回结果
     * @param mappedStatement
     * @param parameterMap
     * @return
     */
    public Object execute(MappedStatement mappedStatement, Map<String,Object> parameterMap){
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        Object res = null;
        String sql = mappedStatement.getSql();
        sql = sql.trim();
        List<String> keyList = repSql(sql);
        sql = keyList.get(keyList.size()-1);//取出处理好的sql语句
        keyList.remove(keyList.size()-1);//将sql语句从list集合中删除
        System.out.println("sql:"+sql+",   param:"+parameterMap);// 输出sql语句和参数
        try {
            connection = pooledDataSource.getConnection();
            preparedStatement = connection.prepareStatement(sql);// 预编译sql
            // 设置参数,根据key设置对应的参数
            for (int i = 0; i < keyList.size(); i++) {
                String key = keyList.get(i);
                Object value = parameterMap.get(key);
                if(value instanceof Integer){
                    preparedStatement.setInt(i+1,(Integer)value);
                }else if(value instanceof String){
                    preparedStatement.setString(i+1,value.toString());
                }else if(value instanceof Boolean){
                    preparedStatement.setBoolean(i+1,(Boolean)value);
                }
            }

            //  执行sql语句
            if(sql.startsWith("select") || sql.startsWith("SELECT")){//查询使用executeQuery()方法
                resultSet = preparedStatement.executeQuery();
                // 处理结果,把resustSet的结果集映射到java对象
                res = mappingResultSet(resultSet, mappedStatement.getResultType());
            }else {// 增删改使用executeUpdate()方法
                res = preparedStatement.executeUpdate();
            }
            return res;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 关闭 resultSet,preparedStatement,connection
            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();
                }
            }
        }
        return null;
    }

    /**
     * 从mapper配置文件中读取到的sql语句需要处理,#{id,jdbcType=INTEGER}这种表达式jdbctemplate是无法解析的
     * 需要把表达式替换为?占位符
     * @param sql
     * @return
     */
    public List<String> repSql(String sql) {
        List<String> keyList = new ArrayList<>();
        while (true){
            int a = sql.indexOf("#{");
            if(a == -1) break;
            int b = sql.indexOf("}");
            String paramE = sql.substring(a, b + 1).replace(" ","");
            sql = sql.replace(paramE, "?");// 将#{}替换为?占位符
            int c = paramE.indexOf(",");
            String key = paramE.substring(2, c);
            keyList.add(key);
        }
        keyList.add(sql);// 将替换为占位符的sql语句放到集合中
        return keyList;
    }



    // 封装结果集
    public Object mappingResultSet(ResultSet resultSet,String resultType){
        // 查询结果可能是一个或者多个,这里使用list来存放结果
        List resList = new ArrayList();
        try{
            Class<?> clazz = Class.forName(resultType);
            if(resultSet.next()){
                // 根据返回类型,使用反射来生成一个对象
                Object entity = clazz.newInstance();
                // 为新生成的对象的属性赋值
                Reflection.setPropertiesToBeanFromResultSet(entity,resultSet);
                resList.add(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resList;
    }

}

3.2.2.1.1 实现一个连接池

package com.salulu.baits.datasource;

import com.salulu.baits.config.Environment;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class PooledDataSource {

    private final LinkedList<Connection> pool =new LinkedList<>();

    private int minSize = 5;

    private Environment environment;

    public PooledDataSource(Environment environment) {
        this.environment = environment;
        initPool();
    }

    private void initPool(){
        try {
            Class.forName(environment.getDriver());
            for (int i = 0; i < minSize; i++) {// 这里创建一个固定大小的连接池
                Connection conn = DriverManager.getConnection(environment.getUrl(), environment.getUsername(), environment.getPassword());
                Connection connProxy = (Connection)Proxy.newProxyInstance(this.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object obj, Method method, Object[] objects) throws Throwable {
                        // 对连接对象的close()方法进行覆盖,不要真的关闭连接,而是把连接放回池中
                        if(method.getName().equals("close")){
                            pool.addLast(conn);
                            return null;
                        }else {
                            return method.invoke(conn,objects);
                        }
                    }
                });
                pool.add(connProxy);
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }

    // 从池中获取一个连接对象
    public Connection getConnection(){
        Connection result =null;
        synchronized (pool) {
            if(pool.size()>0){
                result = pool.removeFirst();
            }
        }
        return result;
    }


}

3.2.2.1.2 为新生成的对象的属性赋值这个过程:新建一个类(Reflection),封装成一个方法

package com.salulu.baits.reflection;

import java.lang.reflect.Field;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

public class Reflection {


    public static void setPropertiesToBeanFromResultSet(Object entity, ResultSet resultSet){
        // 根据结果集,获取到数据库表的元数据信息,这个信息里有详细的表的列,字段信息
        try {
            ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
            // 字段个数
            int columnCount = resultSetMetaData.getColumnCount();

            // 获取到对象所声明的成员变量
            Field[] fieldArray = entity.getClass().getDeclaredFields();

            for (int i=0;i<columnCount;i++) {
                String columnName = resultSetMetaData.getColumnName(i + 1);
                columnName = columnName.replace("_", "");
                for (int j = 0; j < fieldArray.length; j++) {
                    // 对象中的成员变量
                    Field field = fieldArray[j];
                    field.setAccessible(true);
                    String name = field.getName();
                    if(name.equals(columnName)){
                        String fieldTypeName = field.getType().getSimpleName();
                        if(fieldTypeName.equals("Integer")){
                            int columnValue = resultSet.getInt(columnName);
                            field.set(entity,columnValue);
                        }else if(fieldTypeName.equals("Long")){
                            long columnValue = resultSet.getLong(columnName);
                            field.set(entity,columnValue);
                        }else if(fieldTypeName.equals("Float")){
                            float columnValue = resultSet.getFloat(columnName);
                            field.set(entity,columnValue);
                        }else if(fieldTypeName.equals("Double")){
                            double columnValue = resultSet.getDouble(columnName);
                            field.set(entity,columnValue);
                        }else if(fieldTypeName.equals("String")){
                            String columnValue = resultSet.getString(columnName);
                            field.set(entity,columnValue);
                        }else if(fieldTypeName.equals("Date")){
                            Date columnValue = resultSet.getDate(columnName);
                            field.set(entity,columnValue);
                        }else if(fieldTypeName.equals("Boolean")){
                            boolean columnValue = resultSet.getBoolean(columnName);
                            field.set(entity,columnValue);
                        }else{
                            Object object = resultSet.getObject(columnName);
                            System.out.println("实体类中的字段类型为非基本类型,获取到的值为:"+object.toString());
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

3.2.2.2 创建一个SqlSession对象,并传入执行器用来执行sql

package com.salulu.baits.session;

import com.salulu.baits.config.Configuration;
import com.salulu.baits.config.MappedStatement;
import com.salulu.baits.executor.Executor;
import com.salulu.baits.proxy.MapperProxy;
import java.lang.reflect.Proxy;
import java.util.Map;

public class SqlSession {

    private Configuration configuration;
    private Executor executor;

    /**
     * 传入配置信息(可以从配置信息中获取sql语句)
     * 传入执行器,用来执行sql
     * @param configuration
     * @param executor
     */
    public SqlSession(Configuration configuration, Executor executor) {
        this.configuration = configuration;
        this.executor = executor;
    }

    /**
     * 获取mapper的方法,因为mapper是一个接口,这里使用反射,为mapper接口生成一个实现类返回
     * @param interfaceClass
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> interfaceClass) {
        MapperProxy mapperProxy = new MapperProxy(this);
        T t = (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class[]{interfaceClass},
                mapperProxy);
        return t;
    }

    public Object execute(String sqlId, Map<String,Object> parameterMap){
        MappedStatement mappedStatement = configuration.getMappedStatementMap().get(sqlId);
        Object res = executor.execute(mappedStatement, parameterMap);
        return res;
    }

}

3.2.2.2.1 使用反射,为mapper接口生成一个实现类 的代码实现:

新建一个MapperProxy类,实现InvocationHandler接口:

package com.salulu.baits.proxy;

import com.salulu.baits.annotations.Param;
import com.salulu.baits.session.SqlSession;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MapperProxy implements InvocationHandler {

    private SqlSession sqlSession;

    public MapperProxy(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        String methodName = method.getName();
        String sqlId = method.getDeclaringClass().getName()+"."+ methodName;
        Map<String,Object> parameterMap = null;

        // 处理一个或多个非User类型参数
        // 从@Param注解中获取参数与sql配置文件中的对应关系(强制要求使用@Param来标示参数对应的是那个字段)
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if(parameterAnnotations!=null && parameterAnnotations.length>0
                && parameterAnnotations[0]!=null && parameterAnnotations[0].length>0){
            parameterMap = new HashMap<>();
            for(int i =0;i<parameterAnnotations.length;i++){
                Annotation[] parameterAnnotation = parameterAnnotations[i];
                Param param = (Param) parameterAnnotation[0];
                parameterMap.put(param.value(),objects[i]);
            }
        }else{
            // 处理新增方法的实体类类型的参数,比如User类
            // 将User类的数据信息封装为一个Map
            parameterMap = new HashMap<>();
            Object entity = objects[0];
            Field[] fields = entity.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                String name = field.getName();
                Object value = field.get(entity);
                parameterMap.put(name,value);
            }
        }

        Object object = sqlSession.execute(sqlId, parameterMap);
        Class<?> returnType = method.getReturnType();
        String returnTypeSimpleName = returnType.getSimpleName();
        // 返回类型returnTypeSimpleName,一般为list,实体类对象,还有int型
        // 由于执行sql的返回结果默认是返回list和int型的,如果使用实体类接受数据的话会发生类型转换异常
        // 这里需要特殊处理一下
        if( !"List".equals(returnTypeSimpleName) && !(object instanceof Integer)){
            // 不是list也不是int,说明返回的是实体类型,比如说User对象,那么可以从list中获取第一个数据(如果存在的话)
            if(object!=null){
                List list = (List) object;
                if(list!=null && list.size()>0){// 如果存在则获取,如果不存在则返回null即可
                    object = list.get(0);
                }
            }
        } 
        return object;
    }
}

@Param注解类:

package com.salulu.baits.annotations;

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Param {
    String value();
}

构建session工厂的使用:

// 1,读取配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 2,构建session工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

 

3.3 打开sqlsession

SqlSessionFactory类中提供了openSession()方法

上文代码中有写到:

public SqlSession openSession() {
        // 创建一个执行器
        Executor executor = new Executor(configuration);
        // 创建一个SqlSession对象,并传入执行器用来执行sql
        SqlSession session = new SqlSession(configuration, executor);
        return session;
    }

3.4 获取mapper接口对象

SqlSession这个类中有实现这个方法,上文代码中有写到过:

/**
     * 获取mapper的方法,因为mapper是一个接口,这里使用反射,为mapper接口生成一个实现类返回
     * @param interfaceClass
     * @param <T>
     * @return
     */
    public <T> T getMapper(Class<T> interfaceClass) {
        MapperProxy mapperProxy = new MapperProxy(this);
        T t = (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class[]{interfaceClass},
                mapperProxy);
        return t;
    }

3.5 使用获取到的mapper对象执行方法,如selectByPrimaryKey();insert();deleteByPrimaryKey();selectByName();

package com.salulu.test;

import com.salulu.baits.io.Resources;
import com.salulu.baits.session.SqlSession;
import com.salulu.baits.session.SqlSessionFactory;
import com.salulu.baits.session.SqlSessionFactoryBuilder;
import com.salulu.test.entity.User;
import com.salulu.test.mapper.UserMapper;

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

public class Test1 {


    public static void main(String[] args) {
        // 1,读取配置文件
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 2,构建session工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 3,打开sqlsession
        SqlSession session = sqlSessionFactory.openSession();
        // 4,获取mapper接口对象
        UserMapper userMapper = session.getMapper(UserMapper.class);
        // 5,调用mapper对象的方法类执行sql,对数据库进行操作
        int i = userMapper.deleteByPrimaryKey(3);
        List<User> xiaobaiUserList = userMapper.selectByName("小白");
        System.out.println("xiaobaiUserList:"+xiaobaiUserList);
        User user2 = userMapper.selectByPrimaryKey(2);
        System.out.println("user2:"+user2);
        List<User> userList = userMapper.selectByNameAndAge("小萨", 17);
        System.out.println("userList:"+userList);
    }

}

4,测试结果:

可以看对对于单表操作可以正常进行。

 

(代码已上传,在文章顶部mybatis-salulu.zip文件)

 

 

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值