原始的JDBC操作数据库,请参考Java JDBC连接Oracle
一、工程搭建
工程结构如下
1、引入jar mybatis-3.2.3.jar 和mysql-connector-java-5.1.25.bin.jar
2、使用Mysql创建表
CREATE TABLE `user` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(64) NOT NULL DEFAULT '', `dept` VARCHAR(254) NOT NULL DEFAULT '', `website` VARCHAR(254) DEFAULT '', `phone` VARCHAR(16) NOT NULL DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=2 ; INSERT INTO `user` VALUES ('1', 'larry', 'IT', 'http://www.baidu.com', '13688889999');
3、创建User实体类
public class User { private int id; private String name; private String dept; private String phone; private String website; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDept() { return dept; } public void setDept(String dept) { this.dept = dept; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getWebsite() { return website; } public void setWebsite(String website) { this.website = website; } }
4、resources/configs/下创建Configure.xml
mybatis 配置文件,用来建立sessionFactory。里面主要包含了数据库连接,还有Java类对应的别名
<?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> <typeAliases> <typeAlias alias="User2" type="com.example.mybatis.model.User" /> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/User.xml" /> </mappers> </configuration>
这里<mapper resource="mapper/User.xml" /> 要映射类的xml配置文件
5、resources/mapper下创建User.xml
包含了各种SQL语句
<?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.example.mybatis.dao.UserMapper"> <select id="getUserById" parameterType="int" resultType="User2"> select * from user where id = #{id} </select> </mapper>
6、创建接口com.example.mybatis.dao.UserMapper
public interface UserMapper { User getUserById(int id); }
7、创建测试类
public class MyBatisTest1 { private static SqlSessionFactory sqlSessionFactory; private static Reader reader; static { try { reader = Resources.getResourceAsReader("config/Configure.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); }catch (Exception e){ e.printStackTrace(); } } public static SqlSessionFactory getSession(){ return sqlSessionFactory; } public static void main(String[] args) { SqlSession session = sqlSessionFactory.openSession(); try { User user = (User)session.selectOne("com.example.mybatis.dao.UserMapper.getUserById",1 ) ; if(user != null){ System.out.println("name is : " + user.getName() + ",所属部门: " + user.getDept()); } }finally { session.close(); } } }
8、显示
二、源码解析
通过前面的步骤,关键步骤如下
// 1.读取mybatis配置文件,并生成SQLSessionFactory reader = Resources.getResourceAsReader("config/Configure.xml"); // 2.获取session,主要的CRUD操作均在SqlSession中提供 SqlSession session = sqlSessionFactory.openSession(); // 3.执行查询操作 // 该方法包括三个步骤:封装入参数、执行查询、封装结果为对象类型 User user = (User)session.selectOne("com.example.mybatis.dao.UserMapper.getUserById",1 ) ;
1、 Resources.getResourceAsReader("config/Configure.xml");
public static Reader getResourceAsReader(String resource) throws IOException { InputStreamReader reader; //默认为null if (charset == null) { reader = new InputStreamReader(getResourceAsStream(resource)); } else { reader = new InputStreamReader(getResourceAsStream(resource), charset); } return reader; } //getResourceAsStream方法 public static InputStream getResourceAsStream(String resource) throws IOException { return getResourceAsStream((ClassLoader)null, resource); } //getResourceAsStream方法 public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource); } else { return in; } } //ClassLoaderWrapper.getResourceAsStream方法 InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { if (null != cl) { // 获得资源 InputStream returnValue = cl.getResourceAsStream(resource); // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource if (null == returnValue) returnValue = cl.getResourceAsStream("/" + resource); if (null != returnValue) return returnValue; } } return null; }
总结:通过ClassLoader.getResourceAsStream()获取指定路径下的Resource
2、获取sqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); //build方法 public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); //重点是这个方法 实现了parser.parse()解析xml,build创建sqlSessionFactory return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } }
1) 首先是parser.parse()方法
public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration")); return configuration; } private void parseConfiguration(XNode root) { try { //是对各个节点进行解析 propertiesElement(root.evalNode("properties")); //issue #117 read properties first typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); settingsElement(root.evalNode("settings")); environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631 databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); //重点看下这个方法 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { //1、获得resource信息,也就是对应的mapper.xml,这里是mapper/User.xml String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { //2.解析对应的User.xml ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); //3、将解析出来的Mapper对象添加到Configuration中 mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
总结:通过解析configuration.xml文件,获取其中的Environment、Setting,mappers下的所有<mapper>解析出来之后添加到Configuration。
2)、生成一个SQLSessionFactory
public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
3、sqlSessionFactory.openSession(); 开启一个sqlSession
public SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); //1、transactionFactory,这里为JdbcTransactionFactory final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); //2、创建一个transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); //3、创建一个Executor 这里为SimpleExecutor,主要的CRUD操作就是再此处完成的 final Executor executor = configuration.newExecutor(tx, execType, autoCommit); //4、将Executor和configuration作为参数,封装到DefaultSqlSession return new DefaultSqlSession(configuration, executor); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
1) TransactionFactory ,主要用来生成transaction的工厂
public interface TransactionFactory { void setProperties(Properties props); Transaction newTransaction(Connection conn); Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); }
2) Transaction
public interface Transaction { Connection getConnection() throws SQLException; void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException; }
可以看到getConnection() 就是返回的java.sql.Connection, 并且还有commint和rollback等操作
3)Executor
该接口定义了CRUD操作
public interface Executor { ResultHandler NO_RESULT_HANDLER = null; int update(MappedStatement ms, Object parameter) throws SQLException; <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException; <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException; List<BatchResult> flushStatements() throws SQLException; void commit(boolean required) throws SQLException; void rollback(boolean required) throws SQLException; CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql); boolean isCached(MappedStatement ms, CacheKey key); void clearLocalCache(); void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType); Transaction getTransaction(); void close(boolean forceRollback); boolean isClosed(); }
4、session.selectOne的执行过程
public <T> T selectOne(String statement, Object parameter) { // 调用selectList List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } } public <E> List<E> selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT); } public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { //1、获取MappedStatement,里面封装了每一个CRUD操作对应的详细信息 MappedStatement ms = configuration.getMappedStatement(statement); //2、executor的实现类为CachingExecutor List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); return result; } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
5、CachingExecutor.query方法
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) { ensureNoOutParams(ms, parameterObject, boundSql); if (!dirty) { cache.getReadWriteLock().readLock().lock(); try { @SuppressWarnings("unchecked") List<E> cachedList = (List<E>) cache.getObject(key); if (cachedList != null) return cachedList; } finally { cache.getReadWriteLock().readLock().unlock(); } } List<E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks return list; } } //重点是这句,默认实现为BaseExecutor return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); } //BaseExecutor的query方法 public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) throw new ExecutorException("Executor was closed."); if (queryStack == 0 && ms.isFlushCacheRequired()) { clearLocalCache(); } List<E> list; try { queryStack++; list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { //重点是这里 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } deferredLoads.clear(); // issue #601 if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { clearLocalCache(); // issue #482 } } return list; } private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; localCache.putObject(key, EXECUTION_PLACEHOLDER); try { //重点是这里,是一个抽象方法,由SimpleExecutor实现 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; } //SimpleExecutor的doQuery方法 public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); //1、StatementHandler 分装了JDBC Statement操作,如设置参数,将Statement结果转换成List集合 StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql); //2、封装StatementHandler 参数 stmt = prepareStatement(handler, ms.getStatementLog()); //执行execute操作 return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
1) configuration.newStatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; } //RoutingStatementHandler构造方法 public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: //默认实现 delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); } }
2) prepareStatement
功能: 获取Statement; 封装PreparedStatement参数
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; //1、获取java.sql.Connection Connection connection = getConnection(statementLog); //2、获取对应的statement stmt = handler.prepare(connection); //3、封装参数 handler.parameterize(stmt); return stmt; }
*进入handler.prepare(connection)获取Statement
//BaseStatementHandler.prepare public Statement prepare(Connection connection) throws SQLException { ErrorContext.instance().sql(boundSql.getSql()); Statement statement = null; try { //关键是这里 statement = instantiateStatement(connection); setStatementTimeout(statement); setFetchSize(statement); return statement; } catch (SQLException e) { closeStatement(statement); throw e; } catch (Exception e) { closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + e, e); } } //BaseStatementHandler.instantiateStatement protected Statement instantiateStatement(Connection connection) throws SQLException { String sql = boundSql.getSql(); if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) { String[] keyColumnNames = mappedStatement.getKeyColumns(); if (keyColumnNames == null) { return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS); } else { return connection.prepareStatement(sql, keyColumnNames); } } else if (mappedStatement.getResultSetType() != null) { return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY); } else { //默认实现,直接对应传统JDBC方式的从connection中获取PreparedStatement return connection.prepareStatement(sql); } }
* handler.parameterize(stmt);封装参数
public void parameterize(Statement statement) throws SQLException { delegate.parameterize(statement); } public void parameterize(Statement statement) throws SQLException { parameterHandler.setParameters((PreparedStatement) statement); } public void setParameters(PreparedStatement ps) throws SQLException { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); //1、parameterMappings 包含了需要拼装的参数 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); if (parameterMappings != null) { MetaObject metaObject = parameterObject == null ? null : configuration.newMetaObject(parameterObject); for (int i = 0; i < parameterMappings.size(); i++) { ParameterMapping parameterMapping = parameterMappings.get(i); if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; //2、获取参数名称 String propertyName = parameterMapping.getProperty(); //3、获取参数值 if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { value = metaObject == null ? null : metaObject.getValue(propertyName); } //4、获取参数对应的类型处理器 TypeHandler typeHandler = parameterMapping.getTypeHandler(); JdbcType jdbcType = parameterMapping.getJdbcType(); if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull(); //5、对应的封装参数操作还是委托给TypeHandle处理 typeHandler.setParameter(ps, i + 1, value, jdbcType); } } } } //BaseTypeHandler.setParameter public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException { if (parameter == null) { if (jdbcType == null) { throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters."); } try { ps.setNull(i, jdbcType.TYPE_CODE); } catch (SQLException e) { throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " + "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " + "Cause: " + e, e); } } else { //由于参数非空,走这个方法。setNonNullParameter是抽象方法,由子类实现 setNonNullParameter(ps, i, parameter, jdbcType); } }
这里的参数类型为int,对应的实现类为为IntegerTypeHandler
@Override public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) throws SQLException { ps.setInt(i, parameter); }
对应JDBC的处理为preparedStatement.setInt(index,value)
经过上面的分析,最终处理还是JDBC那一套。通过connection创建preparedStatement;对preparedStatement进行参数封装;
3) handler.<E>query(stmt, resultHandler);
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { return delegate.<E>query(statement, resultHandler); } //PreparedStatementHandler.query() public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; //1、执行execute操作 ps.execute(); //2、对结果进行封装 return resultSetHandler.<E> handleResultSets(ps); }
总结:mybatis操作流程如下
1) 解析configuration.xml,生成Environment、Setting、Mapper等对象,并注册到Configuration
2) 从SqlSessionFactory中获取SqlSession,SqlSession作为操作入口,接收用户的数据库操作,并委托给内部的Executor来实现
3) Executor内部StatementHandler负责Statement的创建;PreparedStatement参数的注入;execute方法的执行
4) execute方法执行,ResultSetHandler进行结果的封装
作者:Work Hard Work Smart
出处:http://www.cnblogs.com/linlf03/
欢迎任何形式的转载,未经作者同意,请保留此段声明!