MyBatis(四):自定义持久层框架优化

本文所有代码已上传至码云:https://gitee.com/rangers-sun/mybatis

  1. 修改IUserDao、UserMapper.xml

    package com.rangers;
    
    import com.rangers.entity.User;
    
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description 用户数据库访问Dao
     * @Date 2021-03-07
     **/
    public interface IUserDao {
    
        /**
         * @Author Rangers
         * @Description 查询所有用户信息
         * @Date 2021/3/7 10:43 上午
         * @Return: java.util.List<com.rangers.entity.User>
         **/
        public List<User> selectList();
    
        /**
         * @Author Rangers
         * @Description 根据条件查询单个用户信息
         * @Date 2021/3/7 10:43 上午
         * @Param user:
         * @Return: com.rangers.entity.User
         **/
        public User selectOne(User user) ;
    
        Integer insert(User user);
    
        Integer update(User user);
    
        Integer delete(User user);
    }
    
    <mapper namespace="com.rangers.IUserDao">
    
        <select id="selectOne" parameterType="com.rangers.entity.User" resultType="com.rangers.entity.User">
            select * from user where id=#{id} and name=#{name}
        </select>
    
        <select id="selectList" parameterType="com.rangers.entity.User" resultType="com.rangers.entity.User">
            select * from user
        </select>
    
        <insert id="insert" parameterType="com.rangers.entity.User" resultType="java.lang.Integer">
            insert into user(id,name,address) values(#{id},#{name},#{address})
        </insert>
    
        <update id="update" parameterType="com.rangers.entity.User" resultType="java.lang.Integer">
            update user set name=#{name},address=#{address} where id=#{id}
        </update>
    
        <delete id="delete" parameterType="com.rangers.entity.User" resultType="java.lang.Integer">
            delete from user where id=#{id}
        </delete>
    </mapper>
    
  2. 新增SqlCommondType 修改MappedStatement、XmlMapperBuilder

    package com.rangers.persistent.config;
    
    /**
     * @Author Rangers
     * @Description
     * @Date 2021-03-08
     **/
    public enum SqlCommondType {
    
        INSERT, DELETE, UPDATE, SELECT;
    }
    
    package com.rangers.persistent.config.pojo;
    
    import com.rangers.persistent.config.SqlCommondType;
    
    /**
     * @Author Rangers
     * @Description 对应Mapper中标签的信息
     * @Date 2021-03-04
     **/
    public class MappedStatement {
        // id当前xml中的唯一标识
        private String id;
    
        // sql语句
        private String sql;
    
        // 参数类型
        private String paramterType;
    
        // 返回结果类型
        private String resultType;
    
        // 返回结果类型
        private SqlCommondType sqlCommondType;
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getSql() {
            return sql;
        }
    
        public void setSql(String sql) {
            this.sql = sql;
        }
    
        public String getParamterType() {
            return paramterType;
        }
    
        public void setParamterType(String paramterType) {
            this.paramterType = paramterType;
        }
    
        public String getResultType() {
            return resultType;
        }
    
        public void setResultType(String resultType) {
            this.resultType = resultType;
        }
    
        public SqlCommondType getSqlCommondType() {
            return sqlCommondType;
        }
    
        public void setSqlCommondType(SqlCommondType sqlCommondType) {
            this.sqlCommondType = sqlCommondType;
        }
    }
    
    package com.rangers.persistent.config;
    
    import com.rangers.persistent.config.pojo.Configuration;
    import com.rangers.persistent.config.pojo.MappedStatement;
    import org.apache.commons.collections.CollectionUtils;
    import org.dom4j.Document;
    import org.dom4j.DocumentException;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description 解析XxMapper.xml
     * @Date 2021-03-04
     **/
    public class XmlMapperBuilder {
        private Configuration configuration;
    
        public XmlMapperBuilder(Configuration configuration) {
            this.configuration = configuration;
        }
    
        public void parse(InputStream inputStream) {
            Document document = null;
            try {
                document = new SAXReader().read(inputStream);
            } catch (DocumentException e) {
                e.printStackTrace();
            }
            Element rootElement = document.getRootElement();
    
            String namespace = rootElement.attributeValue("namespace");
    
            List<Element> elements = new ArrayList<>();
            List<Element> selectElements = rootElement.selectNodes("//select");
            elements.addAll(selectElements);
            List<Element> insertElements = rootElement.selectNodes("//insert");
            elements.addAll(insertElements);
            List<Element> updateElements = rootElement.selectNodes("//update");
            elements.addAll(updateElements);
            List<Element> deleteElements = rootElement.selectNodes("//delete");
            elements.addAll(deleteElements);
            // 解析XxMapper.xml中的标签
            this.parseLabel(namespace, elements);
        }
    
        private void parseLabel(String namespace, List<Element> selectElements) {
            if (CollectionUtils.isNotEmpty(selectElements)){
                selectElements.forEach(mapperElement->{
                    String id = mapperElement.attributeValue("id");
                    String sql = mapperElement.getTextTrim();
                    String parameterType = mapperElement.attributeValue("parameterType");
                    String resultType = mapperElement.attributeValue("resultType");
                    String sqlCommondType = mapperElement.getName();
    
                    MappedStatement mappedStatement = new MappedStatement();
                    mappedStatement.setId(id);
                    mappedStatement.setSql(sql);
                    mappedStatement.setParamterType(parameterType);
                    mappedStatement.setResultType(resultType);
                    mappedStatement.setSqlCommondType(SqlCommondType.valueOf(sqlCommondType.toUpperCase()));
                    configuration.getMappedStatementMap().put(namespace +"."+id,mappedStatement);
                });
            }
        }
    
    }
    
  3. 修改SqlSession、DefaultSqlSession

    package com.rangers.persistent.sqlSession;
    
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description
     * @Date 2021-03-04
     **/
    public interface SqlSession {
    
        <E> List<E> selectList(String statementId,Object...param) throws Exception;
    
        <T> T selectOne(String statementId,Object...param) throws Exception;
    
        Integer insert(String statementId,Object...param) throws Exception;
    
        Integer update(String statementId,Object...param) throws Exception;
    
        Integer delete(String statementId,Object...param) throws Exception;
    
        void close() throws SQLException;
    
        <T> T getMapper(Class<T> t);
    }
    
    package com.rangers.persistent.sqlSession;
    
    import com.rangers.persistent.config.SqlCommondType;
    import com.rangers.persistent.config.pojo.Configuration;
    import com.rangers.persistent.config.pojo.MappedStatement;
    import com.rangers.persistent.executor.Executor;
    import com.rangers.persistent.executor.SimpleExecutor;
    import org.apache.commons.collections.CollectionUtils;
    
    import java.lang.reflect.*;
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description
     * @Date 2021-03-04
     **/
    public class DefaultSqlSession implements SqlSession{
    
        private Configuration configuration;
    
        public DefaultSqlSession(Configuration configuration) {
            this.configuration = configuration;
        }
    
        private Executor executor = new SimpleExecutor();
    
        @Override
        public <E> List<E> selectList(String statementId, Object... params) throws Exception {
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            return executor.query(configuration,mappedStatement,params);
        }
    
        @Override
        public <T> T selectOne(String statementId, Object... params) throws Exception {
            List<Object> objects = this.selectList(statementId, params);
            if (CollectionUtils.isNotEmpty(objects)){
                if (objects.size() == 1){
                    return (T) objects.get(0);
                }else {
                    throw new Exception("返回多条记录");
                }
            }
            return null;
        }
    
        @Override
        public Integer insert(String statementId, Object... param) throws Exception {
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            return executor.insert(configuration,mappedStatement,param);
        }
    
        @Override
        public Integer update(String statementId, Object... param) throws Exception {
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            return executor.update(configuration,mappedStatement,param);
        }
    
        @Override
        public Integer delete(String statementId, Object... param) throws Exception {
            MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
            return executor.delete(configuration,mappedStatement,param);
        }
    
        @Override
        public void close() throws SQLException {
            executor.close();
        }
    
        /**
         * @Author Rangers
         * @Description 使用JDK动态代理获取执行的Mapper对象
         **/
        @Override
        public <T> T getMapper(Class<T> t) {
            Object o = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[]{t}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 全限定性类名==XxMapper.xml的namespace
                    String className = method.getDeclaringClass().getName();
                    // 接口的方法名称==XxMapper.xml的每个mapper标签的id
                    String methodName = method.getName();
    
                    // 1、构造statementID:namespace+"."+id
                    String statementId = className+"."+methodName;
    
                    // 2、定位方法执行,获取执行结果
                    Object result = new Object();
                    MappedStatement mappedStatement = configuration.getMappedStatementMap().get(statementId);
                    SqlCommondType sqlCommondType = mappedStatement.getSqlCommondType();
                    switch (sqlCommondType){
                        case INSERT:
                            result = insert(statementId,args);
                            break;
                        case DELETE:
                            result = delete(statementId,args);
                            break;
                        case UPDATE:
                            result = update(statementId,args);
                            break;
                        case SELECT:
                            // 获取被调用方法的返回值
                            Type returnType = method.getGenericReturnType();
                            if (returnType instanceof ParameterizedType){
                                result = selectList(statementId, args);
                            }else{
                                result = selectOne(statementId,args);
                            }
                            break;
                        default:
                            throw new Exception("statementId:"+statementId+",操作类型:"+sqlCommondType+"有误");
                    }
                    return result;
                }
            });
            return (T) o;
        }
    }
    
    
  4. 修改Executor、SimpleExecutor

  5. package com.rangers.persistent.executor;
    
    import com.rangers.persistent.config.pojo.Configuration;
    import com.rangers.persistent.config.pojo.MappedStatement;
    
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description
     * @Date 2021-03-04
     **/
    public interface Executor {
    
        <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object...param) throws Exception;
    
        Integer insert(Configuration configuration, MappedStatement mappedStatement, Object...param) throws Exception;
    
        Integer update(Configuration configuration, MappedStatement mappedStatement, Object...param) throws Exception;
    
        Integer delete(Configuration configuration, MappedStatement mappedStatement, Object...param) throws Exception;
    
        void close() throws SQLException;
    }
    
    package com.rangers.persistent.executor;
    
    import com.mchange.v2.c3p0.impl.NewProxyPreparedStatement;
    import com.rangers.persistent.config.pojo.Configuration;
    import com.rangers.persistent.config.pojo.MappedStatement;
    import com.rangers.persistent.utils.GenericTokenParser;
    import com.rangers.persistent.utils.ParameterMapping;
    import com.rangers.persistent.utils.ParameterMappingTokenHandler;
    import org.apache.commons.collections.CollectionUtils;
    
    import java.beans.PropertyDescriptor;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.sql.*;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description
     * @Date 2021-03-04
     **/
    public class SimpleExecutor implements Executor {
    
        private Connection connnection = null;
    
        @Override
        public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
            PreparedStatement preparedStatement = this.getPreparedStatement(configuration, mappedStatement, params);
    
            // 5、执行SQL语句
            ResultSet resultSet = preparedStatement.executeQuery();
    
            // 6、封装返回结果集
            // 获取SQL的执行结果类型
            String resultType = mappedStatement.getResultType();
            Class<?> resultTypeClazz = getClassType(resultType);
            List<E> results = new ArrayList<>();
            // 遍历封装结果集为返回值类型
            while (resultSet.next()) {
                // 获取结果集的元数据
                ResultSetMetaData metaData = resultSet.getMetaData();
                E e = (E) resultTypeClazz.newInstance();
                if (metaData.getColumnCount() > 0) {
                    for (int i = 1; i <= metaData.getColumnCount(); i++) {
                        // 字段名称
                        String columnName = metaData.getColumnName(i);
                        // 字段值
                        Object columnValue = resultSet.getObject(columnName);
    
                        // 使用反射或者内省,根据数据库表与实体的对应关系,完成结果集封装
                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,resultTypeClazz);
                        Method writeMethod = propertyDescriptor.getWriteMethod();
                        writeMethod.invoke(e,columnValue);
                    }
                }
                results.add(e);
            }
            return results;
        }
    
        @Override
        public Integer insert(Configuration configuration, MappedStatement mappedStatement, Object... param) throws Exception {
            return this.update(configuration,mappedStatement,param);
        }
    
        @Override
        public Integer update(Configuration configuration, MappedStatement mappedStatement, Object... param) throws Exception {
            PreparedStatement preparedStatement = this.getPreparedStatement(configuration, mappedStatement, param);
            return preparedStatement.executeUpdate();
        }
    
        @Override
        public Integer delete(Configuration configuration, MappedStatement mappedStatement, Object... param) throws Exception {
            return this.update(configuration,mappedStatement,param);
        }
    
        private PreparedStatement getPreparedStatement(Configuration configuration, MappedStatement mappedStatement, Object[] params) throws SQLException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
            // 1、获取数据库链接
            connnection = configuration.getDataSource().getConnection();
            // 2、解析SQL语句中的#/$,及参数列表,封装为BoundSql对象
            BoundSql boundSql = getBoundSQL(mappedStatement.getSql());
            // 替换后的SQL语句
            String finalSql = boundSql.getSqlText();
            // 3、获取PreparedStatement对象
            PreparedStatement preparedStatement = connnection.prepareStatement(finalSql);
    
            // 4、设置参数
            // 获取传入参数类型
            String parameterType = mappedStatement.getParamterType();
            Class<?> parameterTypeClazz = getClassType(parameterType);
            // 获取SQL语句中需要替换的的参数列表
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
            if (CollectionUtils.isNotEmpty(parameterMappingList)) {
                for (int i = 0; i < parameterMappingList.size(); i++) {
                    ParameterMapping parameterMapping = parameterMappingList.get(i);
                    // SQL语句中解析出的占位参数名称
                    String content = parameterMapping.getContent();
    
                    // 利用反射从入参列表中获取数据
                    Field declaredField = parameterTypeClazz.getDeclaredField(content);
                    // 设置暴力访问
                    declaredField.setAccessible(true);
                    // 此处默认传入参数类型为对象
                    Object param = declaredField.get(params[0]);
                    preparedStatement.setObject(i + 1, param);
                }
            }
            // 打印执行语句
            this.printSql(preparedStatement);
    
            return preparedStatement;
        }
    
        private void printSql(PreparedStatement preparedStatement) throws NoSuchFieldException, IllegalAccessException {
            NewProxyPreparedStatement tmpPs = (NewProxyPreparedStatement) preparedStatement;
            Field inner = preparedStatement.getClass().getDeclaredField("inner");
            inner.setAccessible(true);
            String sqlLog = inner.get(tmpPs).toString();
            System.out.println("执行SQL:"+sqlLog.substring(sqlLog.lastIndexOf(":")+1));
        }
    
        private Class<?> getClassType(String type) throws ClassNotFoundException {
            if (type != null && type!="") {
                return Class.forName(type);
            }
            return null;
        }
    
        private BoundSql getBoundSQL(String sql) {
            ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
            GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
            String parseSql = genericTokenParser.parse(sql);
            List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
            return new BoundSql(parseSql, parameterMappings);
        }
    
        @Override
        public void close() throws SQLException {
            connnection.close();
        }
    }
    
  6. 拆分测试类直接获取与动态代理

    package com.rangers.mypersistent;
    
    import com.rangers.entity.User;
    import com.rangers.persistent.sqlSession.SqlSession;
    import com.rangers.persistent.sqlSessionFactory.SqlSessionFactory;
    import com.rangers.persistent.sqlSessionFactory.SqlSessionFactoryBuilder;
    import com.rangers.persistent.utils.Resources;
    import org.dom4j.DocumentException;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.beans.PropertyVetoException;
    import java.io.InputStream;
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description
     * @Date 2021-03-04
     **/
    public class MyPersistentTest {
    
        private SqlSession sqlSession;
    
        @Before
        public void before() throws PropertyVetoException, DocumentException {
            // 加载配置文件
            InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 解析XML文件为对象,构造SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
        }
    
        @Test
        public void selectList() throws Exception {
            List<User> users = sqlSession.selectList("com.rangers.IUserDao.selectList", null);
            System.out.println(users.toString());;
        }
    
        @Test
        public void selectOne() throws Exception {
            User user = new User();
            user.setId(2);
            user.setName("莉莉");
            User result = sqlSession.selectOne("com.rangers.IUserDao.selectOne", user);
            System.out.println(result.toString());
        }
    
        @Test
        public void insert() throws Exception {
            User user = new User();
            user.setId(3);
            user.setName("bug");
            user.setAddress("北京");
            boolean insertFlag = sqlSession.insert("com.rangers.IUserDao.insert",user) > 0;
            System.out.println("新增标识:"+insertFlag);
        }
    
        @Test
        public void update() throws Exception {
            User user = new User();
            user.setId(3);
            user.setName("虫子");
            user.setAddress("北京");
            boolean updateFlag = sqlSession.update("com.rangers.IUserDao.update", user)>0;
            System.out.println("修改标识:"+updateFlag);
        }
    
        @Test
        public void delete() throws Exception {
            User user = new User();
            user.setId(3);
            boolean updateFlag = sqlSession.update("com.rangers.IUserDao.delete", user)>0;
            System.out.println("删除标识:"+updateFlag);
        }
    
    
        @After
        public void after() throws SQLException {
            sqlSession.close();
        }
    }
    
    package com.rangers.mypersistent;
    
    import com.rangers.IUserDao;
    import com.rangers.entity.User;
    import com.rangers.persistent.sqlSession.SqlSession;
    import com.rangers.persistent.sqlSessionFactory.SqlSessionFactory;
    import com.rangers.persistent.sqlSessionFactory.SqlSessionFactoryBuilder;
    import com.rangers.persistent.utils.Resources;
    import org.dom4j.DocumentException;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.beans.PropertyVetoException;
    import java.io.InputStream;
    import java.sql.SQLException;
    import java.util.List;
    
    /**
     * @Author Rangers
     * @Description
     * @Date 2021-03-08
     **/
    public class MapperTest {
    
        private SqlSession sqlSession;
    
        @Before
        public void before() throws PropertyVetoException, DocumentException {
            // 加载配置文件
            InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
            // 解析XML文件为对象,构造SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
        }
    
        @Test
        public void mapperSelectOne() throws Exception {
            User userParam = new User();
            userParam.setId(2);
            userParam.setName("莉莉");
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
            User result = userDao.selectOne(userParam);
            System.out.println(result.toString());
        }
    
        @Test
        public void mapperSelectAll() throws Exception {
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
            List<User> result = userDao.selectList();
            System.out.println(result.toString());
        }
    
        @Test
        public void mapperInsert() throws Exception {
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
            User user = new User();
            user.setId(3);
            user.setName("bug");
            user.setAddress("北京");
            boolean insertFlag = userDao.insert(user) > 0;
            System.out.println("新增标识:"+insertFlag);
        }
    
        @Test
        public void mapperUpdate() throws Exception {
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
            User user = new User();
            user.setId(3);
            user.setName("虫子");
            user.setAddress("北京");
            boolean updateFlag = userDao.update(user) > 0;
            System.out.println("修改标识:"+updateFlag);
        }
    
        @Test
        public void mapperDelete() throws Exception {
            IUserDao userDao = sqlSession.getMapper(IUserDao.class);
            User user = new User();
            user.setId(3);
            boolean deleteFlag = userDao.delete(user) > 0;
            System.out.println("删除标识:"+deleteFlag);
        }
    
        @After
        public void after() throws SQLException {
            sqlSession.close();
        }
    }
    
posted @ 2021-03-08 16:39  列兵许三多  阅读(84)  评论(0编辑  收藏  举报