zoukankan      html  css  js  c++  java
  • 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();
          }
      }
      
  • 相关阅读:
    HR人员基本信息、分配信息和地址信息SQL
    iframe下面的session问题
    主流NOSQL数据库之MongoDB快速入门
    CookieThemeResolver
    data binding&&conversionservice
    二进制权限管理(转)
    Spring MVC 对locale和theme的支持
    OpenSessionInViewFilter类作用
    Spring数据库访问之ORM(三)
    Spring自定义属性编辑器PropertyEditorSupport + 使用CustomEditorConfigurer注册属性编辑器
  • 原文地址:https://www.cnblogs.com/rangers-sun/p/14500489.html
Copyright © 2011-2022 走看看