上節講到快速入門mybatis的demo三大階段node
// 1.讀取mybatis配置文件創SqlSessionFactory String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); inputStream.close(); //-------------第二階段------------- // 2.獲取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 3.獲取對應mapper TUserMapper mapper = sqlSession.getMapper(TUserMapper.class); //-------------第三階段------------- // 4.執行查詢語句並返回結果 TUser user = mapper.selectByPrimaryKey(1); System.out.println(user.toString());
第一階段先把配置文件加載到內存,包括數據庫信息和mapper.xml。sql
針對mapper.xml咱們定義一個MappedStatement類來存入相應信息.數據庫
public class MappedStatement { //此處忽略getset方法 private String namespace;//xml裏面的namespace即mapper接口路徑 private String sourceId;//mapper接口路徑+xml裏面的每個id private String sql;//sql語句 private String resultType;//返回類型 }
再定義一個全局配置信息即Configuration存放全部配置信息:api
public class Configuration { //記錄mapper xml文件存放的位置 public static final String MAPPER_CONFIG_LOCATION = "config"; //記錄數據庫鏈接信息文件存放位置 public static final String DB_CONFIG_FILE = "db.properties"; private String dbUrl; private String dbUserName; private String dbPassword; private String dbDriver; //mapper xml解析完之後select節點的信息存放在mappedStatements,key爲MappedStatement裏面 //的sourceId protected final Map<String, MappedStatement> mappedStatements = new HashMap<String, MappedStatement>(); //爲mapper接口生成動態代理的方法 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return MapperProxyFactory.getMapperProxy(sqlSession, type); } }
SqlSessionFactory實例化,並加載configuaration對象信息,這樣就把全部的配置信息加載到內存裏session
public class SqlSessionFactory { //配置對象全局惟一 加載數據庫信息和mapper文件信息 private Configuration conf = new Configuration(); public SqlSessionFactory() { //加載數據庫信息 loadDbInfo(); //加載mapper文件信息 loadMappersInfo(); } private void loadMappersInfo() { URL resources =null; resources = SqlSessionFactory.class.getClassLoader().getResource(conf.MAPPER_CONFIG_LOCATION); File mappers = new File(resources.getFile()); if(mappers.isDirectory()){ File[] listFiles = mappers.listFiles(); for (File file : listFiles) { loadMapperInfo(file); } } } private void loadMapperInfo(File file) { // 建立saxReader對象 SAXReader reader = new SAXReader(); // 經過read方法讀取一個文件 轉換成Document對象 Document document=null; try { document = reader.read(file); } catch (DocumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } //獲取根節點元素對象 Element node = document.getRootElement(); //獲取命名空間 String namespace = node.attribute("namespace").getData().toString(); //獲取select子節點列表 List<Element> selects = node.elements("select"); for (Element element : selects) {//遍歷select節點,將信息記錄到MappedStatement對象,並登記到configuration對象中 MappedStatement mappedStatement = new MappedStatement(); String id = element.attribute("id").getData().toString(); String resultType = element.attribute("resultType").getData().toString(); String sql = element.getData().toString(); String sourceId = namespace+"."+id; mappedStatement.setSourceId(sourceId); mappedStatement.setResultType(resultType); mappedStatement.setSql(sql); mappedStatement.setNamespace(namespace); conf.getMappedStatements().put(sourceId, mappedStatement);//登記到configuration對象中 } } private void loadDbInfo() { InputStream dbIn = SqlSessionFactory.class.getClassLoader().getResourceAsStream(conf.DB_CONFIG_FILE); Properties p = new Properties(); try { p.load(dbIn); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } conf.setDbDriver(p.get("jdbc.driver").toString()); conf.setDbPassword(p.get("jdbc.password").toString()); conf.setDbUrl(p.get("jdbc.url").toString()); conf.setDbUserName(p.get("jdbc.username").toString()); } public SqlSession openSession(){ SqlSession sqlSession = new DefaultSqlSession(conf); return sqlSession; } }
第二階段爲獲取Sqlsession而且從sqlsession獲取mapper動態代理.mybatis
Sqlsessionapp
3.executor基於JDBC訪問數據庫框架
public class DefaultSqlSession implements SqlSession { //配置對象全局惟一 加載數據庫信息和mapper文件信息 private Configuration conf; //真正提供數據庫訪問能力的對象 private Executor executor; public DefaultSqlSession(Configuration conf) { super(); this.conf = conf; executor = new SimpleExecutor(conf); } public <T> T selectOne(String statement, Object parameter) { List<Object> selectList = this.selectList(statement, parameter); if(selectList==null||selectList.size()==0){ return null; } if(selectList.size()==1){ return (T) selectList.get(0); }else { throw new RuntimeException("Too Many Result!"); } } public <E> List<E> selectList(String statement, Object parameter) { MappedStatement mappedStatement = conf.getMappedStatement(statement); try { return executor.query(mappedStatement, parameter); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } @Override //獲取當前mapper接口的動態代理 public <T> T getMapper(Class<T> type) { return conf.<T>getMapper(type, this); } }
Executor是Mybatis核心接口定義了數據庫操做的基本方法,Sqlsession都是基於它來實現的ide
public interface Executor { <E> List<E> query(MappedStatement ms, Object parameter) throws SQLException; <T> T selectOne(String statement,Object parameter);
}學習
Executor實現類:
public class SimpleExecutor implements Executor { private Configuration conf; public SimpleExecutor(Configuration conf) { this.conf = conf; } public <E> List<E> query(MappedStatement ms, Object parameter) throws SQLException { //獲取mappedStatement對象,裏面包含sql語句和目標對象等信息; MappedStatement mappedStatement = conf.getMappedStatement(ms.getSourceId()); //1.獲取Connection對象 Connection conn = getConnect(); //2.實例化StatementHandler對象,準備實例化Statement StatementHandler statementHandler = new DefaultStatementHandler(mappedStatement); //3.經過statementHandler和Connection獲取PreparedStatement PreparedStatement prepare = statementHandler.prepare(conn); //4.實例化ParameterHandler對象,對Statement中sql語句的佔位符進行處理 ParameterHandler parameterHandler = new DefaultParameterHandler(parameter); parameterHandler.setParameters(prepare); //5.執行查詢語句,獲取結果集resultSet ResultSet resultSet = statementHandler.query(prepare); //6.實例化ResultSetHandler對象,對resultSet中的結果集進行處理,轉化成目標對象 ResultSetHandler resultSetHandler = new DefaultResultSetHandler(mappedStatement); return resultSetHandler.handleResultSets(resultSet); } @Override public <T> T selectOne(String statement, Object parameter) { MappedStatement mappedStatement =conf.getMappedStatements().get(statement); return null; } private Connection getConnect() { Connection conn =null; try { Class.forName(conf.getDbDriver()); conn = DriverManager.getConnection(conf.getDbUrl(), conf.getDbUserName(), conf.getDbPassword()); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return conn; } public Configuration getConf() { return conf; } public void setConf(Configuration conf) { this.conf = conf; } }
mapper接口在咱們工程裏面沒有實現類,是經過動態代理來執行方法的.
/** * mapper接口生成動態代理的工程類 * */ public class MapperProxyFactory<T> { public static <T> T getMapperProxy(SqlSession sqlSession,Class<T> mapperInterface){ MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface); return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
InvocationHandler實現類:
public class MapperProxy<T> implements InvocationHandler { private SqlSession sqlSession; private final Class<T> mapperInterface; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) { super(); this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; } private <T> boolean isCollection(Class<T> type) { return Collection.class.isAssignableFrom(type); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) {// 若是是Object自己的方法不加強 return method.invoke(this, args); } Class<?> returnType = method.getReturnType();// 獲取方法的返回參數class對象 Object ret = null; if (isCollection(returnType)) {// 根據不一樣的返回參數類型調用不一樣的sqlsession不一樣的方法 ret = sqlSession.selectList(mapperInterface.getName()+"."+ method.getName(), args); } else { ret = sqlSession.selectOne(mapperInterface.getName()+"."+ method.getName(), args); } return ret; } }
第三階段執行查詢並返回結果.剛剛講過咱們執行數據庫操做其實是executor基於jdbc執行的。
jdbc三大巨頭,Connection,PreparedStatement,ResultSet,
結果集Result再經過反射機制映射到對象上面,便作好了數據的映射(關於映射具體內容可查閱資料及源碼),到這咱們已經完成了一個簡易的Mybatis框架了.
經過手寫一個簡單的Mybatis框架,咱們就能夠看得懂源碼了,學習框架設計的思路而且加強咱們Java的內功.