springmvc+Mybatis整合中sqlSession的创建问题

spring/springmvc+mybatis在整合时,可以在applicationContent.xml文件中进行spring,springmvc,事务管理,数据库连接池等以及与Mybatis整合的配置,当然也可以分开配置各自的xml文件。在mybatis-config.xml中主要进行一些别名,查询的分页方式的配置。例如:

applicationContext.xml中与mybatis的整合配置:

1<!--由mybatis.xml文件构建一个sqlSessionFactory的实例--> 2 <bean id="sqlSessionFactory" class="com.minmate.web.dao.mybatis.SqlSessionFactoryBean"> 3 <property name="configLocation" value="classpath:/config/mybatis/mybatis.xml" /> 4 <!--配置数据源--> 5 <property name="dataSource" ref="dataSource" /> 6 <!--配置实体类的Mapper.xml文件--> 7 <property name="mapperLocations" value="classpath:/config/mybatis/mapper/**/*Mapper.xml" /> 8 </bean> 9 10mybatis.xml中的配置: 11 12<configuration> 13 <typeAliases> 14 <typeAlias alias="Pair" type="com.minmate.util.Pair" /> 15 </typeAliases> 16 <!-- 使用数据库的物理分页方式,iBatis默认分页方式采用游标数据量时候严重影响性能 --> 17 <plugins> 18 <plugin interceptor="com.minmate.web.dao.mybatis.support.OffsetLimitInterceptor"> 19 <property name="dialectClass" value="com.minmate.web.dao.mybatis.support.MySQLPageDialect" /> 20 </plugin> 21 </plugins> 22</configuration>

applicationContext.xml配置mybatis的映射文件,除了用

<property name="mapperLocations" value="classpath:/config/mybatis/mapper/**/*Mapper.xml" />

也可以使用MapperScannerConfigurer扫描basePackage指定的包,找到映射接口类和映射XML文件,并注入:

1<!-- 扫描mybatis映射接口类 -->   2<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">   3    <property name="basePackage" value="com.mybatis.mapper"/>   4    <property name="sqlSessionFactoryBeanName" value="lazySqlSessionFactory"/>   5</bean>

这个配置的前提条件是:映射接口类文件(.java)和映射XML文件(.xml)需要放在相同的包下(com.mybatis.mapper)

接下来主要理理与数据库操作有关的sqlSession是如何产生的。

在我们的项目中创建SqlSessionFactory实例时,使用的SqlSessionFactoryBean类实现了FactoryBean<SqlSessionFactory>,InitializingBean这两个接口,重写了它们的方法。其中FactoryBean接口及各个方法的作用:

1package org.springframework.beans.factory; 2 3public abstract interface FactoryBean<T> 4{ 5  /* 6  返回由 FactoryBean 创建的 Bean 实例,如果 isSingleton() 返回 true , 7  则该实例会放到Spring 容器单实例缓存池中 8  **/ 9  public abstract T getObject() 10    throws Exception; 11  /* 12  返回 FactoryBean 创建的 Bean 类型 13  **/ 14  public abstract Class<?> getObjectType(); 15  /* 16  FactoryBean 创建的 Bean 实例的作用域是 singleton 还是 prototype  17  **/ 18  public abstract boolean isSingleton(); 19}

注:

当配置文件中<bean> 的 class 属性配置的实现类虽然是 SqlSessionFactoryBean,但通过 getBean() 方法返回的不是SqlSessionFactoryBean类本身的对象,而是 FactoryBean.getObject() 方法所返回的对象,相当于 FactoryBean.getObject() 代理了getBean() 方法。例如下面代码是返回了一个sqlSessionFactory对象,而不是SqlSessionFactoryBean类的对象。

1   @Override 2    public SqlSessionFactory getObject() 3    { 4        return sqlSessionFactory; 5    }

如果要得到FactoryBean本身,则需要在使用getBean(beanName)方法时,beanName前加上“&”,如:getBean(“&beanName”)

InitializingBean接口:   

1package org.springframework.beans.factory; 2 3public abstract interface InitializingBean 4{ 5 6  /* 7  在所有属性被设置完之后,容器会调用afterPropertiesSet()方法, 8  应用对象可以在这里执行任何定制的初始化操作(init-method方法与它的区别——不依赖springAPI, 9  从而降低与spring的耦合) 10  **/ 11  public abstract void afterPropertiesSet() 12    throws Exception; 13}

在我们的项目中afterPropertiesSet()方法的重写:

1 @Override 2    public void afterPropertiesSet() throws IOException 3    { 4        if( configLocation==null ) throw new IOException("configLocation in SqlSessionFactoryBean can't be null."); 5 6        this.sqlSessionFactory = createSqlSessionFactory(); 7    }

利用createSqlSessionFactory()方法创建了一个sqlSessionFactory实例,getObject()得到的就是这个sqlSessionFactory。这个产生sqlSessionFactory的方法中关键的代码:

1  //封装配置文件的流 2  reader = new InputStreamReader(new ByteArrayInputStream(XMLHelper.toString(document).getBytes("utf-8")));          3 // 创建sqlSessionFactory 4 sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); 5 //将解析配置文件后的内容放入Configuration 6 Configuration conf = sqlSessionFactory.getConfiguration();

可以看出是通过SqlSessionFactoryBuilder这个类的build方法来创建的,查看一下SqlSessionFactoryBuilder.java的源码,其中就只有build方法的重载,部分实现如下:

1 package org.apache.ibatis.session; 2  3 public SqlSessionFactory build(Reader reader, String environment, Properties props) 4  { 5    try 6    { 7      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, props); 8      Configuration config = parser.parse(); 9      return build(config); 10    } 11    catch (Exception e) 12    { 13      throw ExceptionFactory.wrapException("Error building SqlSession.", e); 14    } 15    finally 16    { 17      ErrorContext.instance().reset(); 18      try 19      { 20        reader.close(); 21      } 22      catch (IOException e) {} 23    } 24  } 25   26  public SqlSessionFactory build(Configuration config) 27  { 28    return new DefaultSqlSessionFactory(config); 29  }

可以看到SqlSessionFactoryBuilder类只是解析了xml文件然后将解析后的内容赋给一个Configuration对象,而真正创建SqlSessionFactory的是DefaultSessionFactory类,它是实现SqlSessionFactory接口的类,SqlSessionFactory接口的源码:

1package org.apache.ibatis.session; 2 3import java.sql.Connection; 4 5public abstract interface SqlSessionFactory 6{ 7  public abstract SqlSession openSession(); 8   9  public abstract SqlSession openSession(boolean paramBoolean); 10   11  public abstract SqlSession openSession(Connection paramConnection); 12   13  public abstract SqlSession openSession(TransactionIsolationLevel paramTransactionIsolationLevel); 14   15  public abstract SqlSession openSession(ExecutorType paramExecutorType); 16   17  public abstract SqlSession openSession(ExecutorType paramExecutorType, boolean paramBoolean); 18   19  public abstract SqlSession openSession(ExecutorType paramExecutorType, TransactionIsolationLevel paramTransactionIsolationLevel); 20   21  public abstract SqlSession openSession(ExecutorType paramExecutorType, Connection paramConnection); 22   23  public abstract Configuration getConfiguration(); 24}

可以看到SqlSessionFactory接口中包含了一系列可以得到SqlSession对象的抽象重载方法,由类DefaultSqlSessionFactory来实现这个接口,其中部分实现以及几个关键的方法为:

1 package org.apache.ibatis.session.defaults; 2  3 ... 4  private static final Log log = LogFactory.getLog(Connection.class); 5  private final Configuration configuration; 6  private final TransactionFactory managedTransactionFactory; 7  /**构造函数初始化*/ 8  public DefaultSqlSessionFactory(Configuration configuration) 9  { 10    this.configuration = configuration; 11    this.managedTransactionFactory = new ManagedTransactionFactory(); 12  } 13  public SqlSession openSession() 14  { 15    return openSessionFromDataSource(this.configuration.getDefaultExecutorType(), null, false); 16  } 17   18  ... 19 20  **********************************关键的方法************************************** 21  /** 22  根据数据源创建session 23  @params execType执行器的类型 level事物隔离级别 autoCommit是否自动提交 24  */ 25  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit){...} 26    /**根据连接对象创建session*/ 27   private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection){...} 28   /**从环境中获取数据源*/ 29    private DataSource getDataSourceFromEnvironment(Environment environment){...} 30    /**从环境配置中获取事务工厂*/ 31    private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment){...} 32    /**包装连接,使连接拥有日志功能*/ 33    private Connection wrapConnection(Connection connection){...}

创建的SqlSession 包含了所有执行数据库SQL 语句的方法,能够直接地通过SqlSession 实例执行映射SQL 语句。其源码接口中定义了数据库操作的一系列抽象方法:

1package org.apache.ibatis.session; 2 3import java.sql.Connection; 4import java.util.List; 5 6public abstract interface SqlSession 7{ 8  public abstract Object selectOne(String paramString); 9   10  public abstract Object selectOne(String paramString, Object paramObject); 11   12  public abstract List selectList(String paramString); 13   14  public abstract List selectList(String paramString, Object paramObject); 15   16  public abstract List selectList(String paramString, Object paramObject, RowBounds paramRowBounds); 17   18  public abstract void select(String paramString, Object paramObject, ResultHandler paramResultHandler); 19   20  public abstract void select(String paramString, Object paramObject, RowBounds paramRowBounds, ResultHandler paramResultHandler); 21   22  public abstract int insert(String paramString); 23   24  public abstract int insert(String paramString, Object paramObject); 25   26  public abstract int update(String paramString); 27   28  public abstract int update(String paramString, Object paramObject); 29   30  public abstract int delete(String paramString); 31   32  public abstract int delete(String paramString, Object paramObject); 33   34  public abstract void commit(); 35   36  public abstract void commit(boolean paramBoolean); 37   38  public abstract void rollback(); 39   40  public abstract void rollback(boolean paramBoolean); 41   42  public abstract void close(); 43   44  public abstract void clearCache(); 45   46  public abstract Configuration getConfiguration(); 47   48  public abstract <T> T getMapper(Class<T> paramClass); 49   50  public abstract Connection getConnection(); 51}

由DefaultSqlSession类来实现该接口,部分源码为:

1package org.apache.ibatis.session.defaults; 2... 3 4public class DefaultSqlSession implements SqlSession 5{ 6  private Configuration configuration; 7  private Executor executor; 8  private boolean autoCommit; 9  private boolean dirty; 10   11  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) 12  { 13    this.configuration = configuration; 14    this.executor = executor; 15    this.autoCommit = autoCommit; 16    this.dirty = false; 17  } 18  ... 19public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) 20  { 21    try 22    { 23      MappedStatement ms = this.configuration.getMappedStatement(statement); 24      this.executor.query(ms, wrapCollection(parameter), rowBounds, handler); 25    } 26    catch (Exception e) 27    { 28      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e); 29    } 30    finally 31    { 32      ErrorContext.instance().reset(); 33    } 34  } 35  ... 36  public int update(String statement, Object parameter) 37  { 38    try 39    { 40      this.dirty = true; 41      MappedStatement ms = this.configuration.getMappedStatement(statement); 42      return this.executor.update(ms, wrapCollection(parameter)); 43    } 44    catch (Exception e) 45    { 46      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e); 47    } 48    finally 49    { 50      ErrorContext.instance().reset(); 51    } 52  } 53  ... 54}

可以看到真正对数据库进行操作的是executor对象,org.apache.ibatis.executor包中的Executor接口封装了数据库的操作方法,这个接口和它的实现类的关系:

执行器(Executor)类型只有三种

SIMPLE:普通的执行器;

REUSE:执行器会重用预处理语句(prepared statements);

BATCH:执行器将重用语句并执行批量更新。

具体类型的指定是在用openSession()方法创建sqlSession传入参数指定的,执行器的实现类里面封装了最原始的JDBC操作。

了解了sqlSession的整个产生过程,看看我们的项目中是怎么来使用的。在我们的项目中用一个MyBatisDaoSupport类来执行DAO层的操作:

1public class MyBatisDaoSupport extends DaoSupport 2{ 3    protected final Logger log = Logger.getLogger(getClass()); 4     5    /** factory */ 6    private SqlSessionFactory sqlSessionFactory; 7     8    /** session template */ 9    private SqlSessionTemplate sqlSessionTemplate; 10     11    @Resource 12    private SqlSessionFactoryBean sqlSessionFactoryBean; 13     14    @PostConstruct 15    public void init() 16    { 17        /**获取sqlSessionFactory*/ 18        this.sqlSessionFactory = sqlSessionFactoryBean.getObject(); 19        /**实例化sqlSession持久化模版*/ 20        this.sqlSessionTemplate = new SqlSessionTemplate(sqlSessionFactory); 21    } 22    ... 23     24     25    /** SqlSessionTemplate */ 26    public static class SqlSessionTemplate 27    { 28        /** factory */ 29        private SqlSessionFactory sqlSessionFactory; 30         31        /** 构造函数 */ 32        public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) 33        { 34            this.sqlSessionFactory = sqlSessionFactory; 35        } 36         37        /** execute */ 38        public Object execute(SqlSessionCallback action) 39        { 40            SqlSession session = null; 41             42            try 43            { 44                /**通过openSession()方法获取session,上面已经说过*/ 45                session = sqlSessionFactory.openSession(); 46                return action.doInSession(session); 47            } 48            finally 49            { 50                if( session!=null ) 51                { 52                    session.commit(); 53                    session.close(); 54                    session = null; 55                } 56            } 57        } 58         59        /** 插入记录 */ 60        public int insert(final String statement) 61        { 62            /**这里的SqlSessionCallback是匿名内部类,用来返回执行sql语句后的结果*/ 63            return (Integer)execute(new SqlSessionCallback() 64            { 65                @Override 66                public Object doInSession(SqlSession session) 67                { 68                    return session.insert(statement); 69                } 70            }); 71        } 72         73        ... 74    } 75     76     77    /** session 回调 */ 78    private static interface SqlSessionCallback 79    { 80        public Object doInSession(SqlSession session); 81    } 82    ... 83

其中在静态内部类SqlSessionTemplate中有一个execute()方法,其使用一个匿名内部类作为参数,该类实际上实现了SqlSessionCallback接口 ,其余的insert、update等方法内都调用了execute()方法将其结果返回,返回的实际上是该匿名内部类实现的接口SqlSessionCallback中doInSession()方法执行后的结果,而在doInSession()方法中就使用sqlSession对象进行sql语句的映射操作。

这段匿名内部类的写法等价于如下代码:

1 /** session 回调 */ 2 private static interface SqlSessionCallback 3 { 4 public Object doInSession(SqlSession session); 5 } 6 private static class SqlSessionCallbackImpl implements SqlSessionCallback 7 { 8 private String statement; Object param; int firstResult, maxResults; 9 SqlSessionCallbackImpl(String statement, Object param, int firstResult, int maxResults) 10 { 11 this.statement = statement; 12 this.firstResult = firstResult; 13 this.maxResults = maxResults; 14 this.param = param; 15 } 16 17 @Override 18 public Object doInSession(SqlSession session) 19 { 20 return session.selectList(statement, param, new RowBounds(firstResult, maxResults)); 21 }; 22 } 23 24 /** 分页参数查询 */ 25 public List<?> selectList(final String statement, final Object param, final int firstResult, final int maxResults) 26 { 27 SqlSessionCallbackImpl sqlSessionCallbackImpl = new SqlSessionCallbackImpl(statement,param,firstResult,maxResults); 28 return (List<?>)execute(sqlSessionCallbackImpl); 29 }
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )