内容简介:本篇为原创文章,如需转载,请标明原创地址。========================================================================该对象是mapper.xml在对象中的体现,是整个mybatis框架中最为核心的对象,我们也可以不必通过xml文件来构建该对象,可以直接通过编码方式构建,像最常用的简单的增删改查操作完全可以手动构建mappedStatement对象并加入到mybatis容器中,这样我们就不需要在xml文件中手写CRUD操作了,mybatis
本篇为原创文章,如需转载,请标明原创地址。
我先写一个简单的例子来执行一条 sql 语句
mapper.xml
<mapper namespace="com.example.demo1.mybatis.ArticleMapper"> <select id="selectById" resultType="com.example.demo1.mybatis.Article" parameterType="java.lang.Long"> select <include refid="baseColumns"/> from article where 1= 1 and id = #{id} </select> <sql id="baseColumns"> id,title </sql> </mapper>
实体类
@Data public class Article { private Long id; private String title; }
测试类
public class MybatisTest { public static void main(String[] args) throws IOException { // sqlSessionFactory是一个复杂对象,通常创建一个复杂对象会使用建造器来构建,这里首先创建建造器 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); // configuration对象对应mybatis的config文件,为了测试简便,我这里直接创建Configuration对象而不通过xml解析获得 Configuration configuration = new Configuration(); configuration.setEnvironment(buildEnvironment()); // 解析一个mapper.xml为MappedStatement并加入到configuration中 InputStream inputStream = Resources.getResourceAsStream("mybatis/Article.xml"); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, "mybatis/Article.xml", configuration.getSqlFragments()); mapperParser.parse(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(configuration); // 创建一个sqlSession,这里使用的是简单工厂设计模式 SqlSession sqlSession = sqlSessionFactory.openSession(); // 执行最终的sql,查询文章id为1的文章 Article article = sqlSession.selectOne("com.example.demo1.mybatis.ArticleMapper.selectById",1L); // 打印文件的标题 System.out.println(article.getTitle()); // sqlSession默认不会自动关闭,我们需要手动关闭 sqlSession.close(); } private static Environment buildEnvironment() { return new Environment.Builder("test") .transactionFactory(getTransactionFactory()) .dataSource(getDataSource()).build(); } private static DataSource getDataSource() { String url = "url"; String user = "user"; String password = "password"; Properties properties = new Properties(); properties.setProperty("url", url); properties.setProperty("username", user); properties.setProperty("password", password); properties.setProperty("driver", "com.mysql.jdbc.Driver"); properties.setProperty("driver.encoding", "UTF-8"); PooledDataSourceFactory factory = new PooledDataSourceFactory(); factory.setProperties(properties); DataSource dataSource = factory.getDataSource(); return dataSource; } private static TransactionFactory getTransactionFactory() { return new JdbcTransactionFactory(); }
分析sqlSession.selectOne("com.example.demo1.mybatis.ArticleMapper.selectById",1L);
public <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. 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; } }
通过查看源码,我们发现不管是查询一条数据还是查询多条数据都是执行的selectList方法,查询一条的时候只要取list的第一条数据即可。
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 { /* 根据statement id找到对应的MappedStatement,而statement id对应的就是mapper的namespace+crud操作的id 在本例中就是com.example.demo1.mybatis.ArticleMapper.selectById */ MappedStatement ms = configuration.getMappedStatement(statement); // 委托执行器来执行查询操作 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); } }
使用执行器来执行查询操作,简单的执行器只会执行sql,并将结果放入到一级缓存中,带二级缓存的执行器会增加一层缓存读写操作,这里先只讨论简单执行器的执行
========================================================================
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { //得到绑定sql BoundSql boundSql = ms.getBoundSql(parameter); //创建缓存Key CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); //查询 return query(ms, parameter, rowBounds, resultHandler, key, boundSql); }
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(); /* 新建一个StatementHandler StatementHandler的作用主要有以下几个: 1.从sqlSource获取最终需要执行的sql 2.创建jdbc的statement对象 3.给statement对象赋值 4.执行statement.execute方法执行赋值的sql 5.通过resultHandler对resultSet结果集进行处理收集,获得最终的结果 6.返回最终的结果 */ StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 通过jdbc连接创建一个全新的prepareStatement,并对其进行赋值,对应上面步骤的2,3 stmt = prepareStatement(handler, ms.getStatementLog()); // 执行赋值后的sql并对结果进行处理收集,对应上面步骤的4,5 return handler.<E>query(stmt, resultHandler); } finally { // 执行statement.close方法 closeStatement(stmt); } }
创建statementHandler对象
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { /* 创建一个路由statementHandler 根据statementType进行路由,根据jdbc的基本知识我们知道常用的statementType有三种: 1.STATEMENT 硬编码的语句,有sql注入风险 2.PREPARED 预编译sql的语句,一般情况下都使用这个 3.CALLABLE 执行存储过程的语句 通常我们使用的都是preparedStatement */ StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); /* 通过上一个步骤statementHandler已经被创建好,preparedStatement也已被初始化 我们得到了一个sql为select id ,title from article where id = ? 的preparedStatement 想一想,如果我们的sql为 select id,title from article 返回多条记录,我们要分页的话怎么办? 一种方法是我们在mapper.xml中在sql语句尾部手动添加limit *,*来进行分页(以 mysql 举例) 另一种方法我们可以通过插件的方式来实现,像常用的PageHelper插件就是基于此来实现的分页功能。 使用插件的好处是将分页功能和sql语句分离,达到去耦合的目的。这样我们切换数据库的时候sql语句并不需要改动。 * */ statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
/** * 这个方法没有什么好分析的,就是执行sql语句,并对结果resultSet进行处理。 * 默认的结果集处理器就是DefaultResultSetHandler,其处理方案就是遍历resultSet集合, * 将所有的数据追加到List<Article>集合中去。 */ @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }
关于MappedStament
该对象是mapper.xml在对象中的体现,是整个mybatis框架中最为核心的对象,我们也可以不必通过xml文件来构建该对象,可以直接通过编码方式构建,像最常用的简单的增删改查操作完全可以手动构建mappedStatement对象并加入到mybatis容器中,这样我们就不需要在xml文件中手写CRUD操作了,mybatis-plus框架设计的思想就是鉴于此。
总结:
其实Mybatis执行一条sql,底层还是用的最基本的jdbc操作,只不过将事物,数据源,参数的设置,结果的收集转换都封装了起来,让我们在开发中专注于sql本身,而忽略那些与业务不相关的步骤(结果对象的映射,开启事物,关闭事物等等操作),提高了项目的内聚性。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 自制小工具大大加速MySQL SQL语句优化(附源码)
- 自制小工具大大加速MySQL SQL语句优化(附源码)
- MySQL 建表语句转 PostgreSQL 建表语句全纪录
- Go语言开发-过程式编程-通信和并发语句-Select语句
- SQL语句优化之JOIN和LEFT JOIN 和 RIGHT JOIN语句的优化
- Python 条件语句
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Usability for the Web
Tom Brinck、Darren Gergle、Scott D. Wood / Morgan Kaufmann / 2001-10-15 / USD 65.95
Every stage in the design of a new web site is an opportunity to meet or miss deadlines and budgetary goals. Every stage is an opportunity to boost or undercut the site's usability. Thi......一起来看看 《Usability for the Web》 这本书的介绍吧!