simple-mybatis的原理及實現

xjz1842發表於2018-06-19

這是對前面分析的程式碼的分析的一個總結並實現一個簡單的例子來加深對框架的理解,首先來看一下mybatis框架的架構圖:

simple-mybatis的原理及實現

可以看到Mybatis框架的核心是MapperStatements的元件,它主要依賴於 Configuration的Mapper,來完成對運算元據庫的操作。

1. 環境的配置

ublic class Configuration {
    /**
     * 配置
     */
    private static final String DEFAULT_CONFIG_LOCATION = "application.properties";

    /**
     * mapper掃描的包路徑
     */
    public static final String MAPPER_SCAN_BASEPACKAGE = "mapper.scan.basepackage";

    /**
     * Mapper的註冊
     */
    private Map<Class<?>, MapperProxy> mapperProxyRegister = new HashMap<>();

    /**
     * mapperClass
     */
    private List<Class<?>> mapperClass = new ArrayList<>();

    /**
     * mapperStatement的註冊
     */
    private Map<String, MapperStatement> mapperStatementRegister = new HashMap<>();

    /**
     * 載入配置服務
     */
    public Properties load() {
        Properties config = new Properties();
        try {
            config.load(Configuration.class.getClassLoader().getResourceAsStream(DEFAULT_CONFIG_LOCATION));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return config;
    }
}
複製程式碼

2.1 Mapper的註冊

 /**
     * 掃描mapper類
     */
    public List<Class<?>> doScanMapper(String packageName) {

        addClass(Thread.currentThread().getContextClassLoader(), packageName, mapperClass);
        /**
         * 掃描包
         */
        for (Class<?> clazz : mapperClass) {
            if (!mapperProxyRegister.containsKey(clazz)) {
                mapperProxyRegister.put(clazz, new MapperProxy());
            }
        }
        return mapperClass;
    }
複製程式碼

2.2 MapperProxy動態代理類的生成

public class MapperProxy implements InvocationHandler {

    private SqlSession sqlSession;
    private Class<?> mapperInterface;
    private  Configuration configuraion;

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        String mapperId = mapperInterface.getName() +"."+ method.getName();
        MapperStatement ms =  configuraion.getMapperStatementRegister().get(mapperId);
        if(ms  == null){
            throw  new RuntimeException("MapperStatement not found");
        }
        //這裡暫時只實現了select操作
        if(SqlCommand.SELECT.equals(ms.getSqlCommand())){
           return sqlSession.selectList(ms,objects);
        }
        return null;
    }

    public <T> T newInstance(Class<?> target) {
        this.mapperInterface = target;
        return (T) Proxy.newProxyInstance(target.getClassLoader(),new Class<?>[]{target}, this);
    }

    public void setSqlSession(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    public void setConfiguraion(Configuration configuraion) {
        this.configuraion = configuraion;
    }
}
複製程式碼

3.1 MapperStatement的註冊

public void registerMapperStatement() {
        //註冊MapperStatement
        for (Class<?> clazz : mapperClass) {
            Method[] methods = clazz.getDeclaredMethods();

            //迴圈遍歷每一個屬性
            for (Method method : methods) {
                MapperStatement mapperStatement = new MapperStatement();
                String mapperId = clazz.getName() + "." + method.getName();
                mapperStatement.setMapperId(mapperId);

                String sql = "";
                if (method.isAnnotationPresent(Select.class)) {
                    sql = method.getAnnotation(Select.class).value();
                    mapperStatement.setSqlCommand(SqlCommand.SELECT);
                } else if (method.isAnnotationPresent(Update.class)) {
                    sql = method.getAnnotation(Update.class).value();
                    mapperStatement.setSqlCommand(SqlCommand.UPDATE);
                }else if (method.isAnnotationPresent(Insert.class)) {
                    sql = method.getAnnotation(Insert.class).value();
                    mapperStatement.setSqlCommand(SqlCommand.INSERT);
                }else if (method.isAnnotationPresent(Delete.class)) {
                    sql = method.getAnnotation(Delete.class).value();
                    mapperStatement.setSqlCommand(SqlCommand.DELETE);
                } else {
                    throw new RuntimeException("no annotaion found");
                }
                mapperStatement.setBoundSql(sql);
                mapperStatement.setParams(method.getParameterTypes());
                mapperStatement.setResult(method.getReturnType());
                mapperStatement.setMethod(method);

                if (!mapperStatementRegister.containsKey(mapperId)) {
                    mapperStatementRegister.put(mapperId, mapperStatement);
                }
            }
        }
    }
複製程式碼

4.1 SqlSession的介面

public interface SqlSession {

   /**
    * 查詢一個list集合
    */
   <T> List<T>  selectList(MapperStatement mapperStatement,Object[] objects);

   /**
    * 查詢一個
    */
   <T> T selectOne(String statement);

   /**
    * 插入一條記錄
    */
   int insert(String statement);

   /**
    * 更新一條記錄
    */
   int update(String statement);

   /**
    * 刪除一條記錄
    */
   int delete(Serializable id);

   /**
    * 獲取Mapper
    */
   <T> T getMapper(Class<?> clazz);
複製程式碼

4.2 SqlsessionFactory的工廠類的實現

public class SqlsessionFactory {

    private DataSource dataSource;
    private Configuration configuration;

    /**
     * 建立者
     */
    public SqlSession build() {
        this.configuration = new Configuration();
        DataSourceFacotry dataSourceFacotry = new JdbcDataSourceFactory();
        dataSourceFacotry.setProperties(configuration.load());
        this.dataSource = dataSourceFacotry.getDataSource();
        //掃描介面包
        configuration.doScanMapper(configuration.getKey(Configuration.MAPPER_SCAN_BASEPACKAGE));

        //註冊mapperStatement
        configuration.registerMapperStatement();

        return new DefalutSqlSession(configuration, dataSource);
    }
複製程式碼

5.1 DataSourceFacoctory的介面及實現

public interface DataSourceFacotry {
    void setProperties(Properties props);
    DataSource getDataSource();
}
public class JdbcDataSourceFactory implements DataSourceFacotry {

    /**
     * url引數
     */
    private static final String DB_URL = "database.url";
    /**
     * 使用者名稱
     */
    private static final String USER = "database.user";
    /**
     * 密碼
     */
    private static final String pwd = "database.password";
    /**
     * driver
     */
    private static final String driver = "db.driver";

    /**
     * 資料來源
     */
    private DruidDataSource dataSource;

    @Override
    public void setProperties(Properties props) {
        dataSource = new DruidDataSource();
        //設定連線引數
        dataSource.setUrl(props.getProperty(DB_URL));
        dataSource.setDriverClassName(props.getProperty(driver));
        dataSource.setUsername(props.getProperty(USER));
        dataSource.setPassword(props.getProperty(pwd));
        //配置初始化大小、最小、最大
        dataSource.setInitialSize(1);
        dataSource.setMinIdle(1);
        dataSource.setMaxActive(20);
        //連線洩漏監測
        dataSource.setRemoveAbandoned(true);
        dataSource.setRemoveAbandonedTimeout(30);
        //配置獲取連線等待超時的時間
        dataSource.setMaxWait(20000);
        //配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
        dataSource.setTimeBetweenEvictionRunsMillis(20000);
        //防止過期
        dataSource.setValidationQuery("SELECT 'x'");
        dataSource.setTestWhileIdle(true);
        dataSource.setTestOnBorrow(true);
    }

    @Override
    public DataSource getDataSource() {
        return dataSource;
    }
複製程式碼

結果演示

    @Test
    public void test(){
        SqlsessionFactory sqlsessionFactory = new SqlsessionFactory();

        SqlSession session = sqlsessionFactory.build();

        UserMapper userMapper = session.getMapper(UserMapper.class);

        List<User> userList = userMapper.selectList();

        System.out.println(userList);
    }
複製程式碼

simple-mybatis的原理及實現

程式碼在github地址:https://gitee.com/xjz1842/simple-mybatis。還有許多不完善的.請多多指正。

相關文章