实现类可以进行增删查改以及事务操作
利用SqlSession内部的方法进行CRUD操作
SqlSession重要的四个对象
1)Execute:调度执行StatementHandler、ParmmeterHandler、ResultHandler执行相应的SQL语句;
2)StatementHandler:使用数据库中Statement(PrepareStatement)执行操作,即底层是封装好了的prepareStatement;
3)ParammeterHandler:处理SQL参数;
4)ResultHandler:结果集ResultSet封装处理返回
在 MyBatis 中,真正干活的是 Executor,我们会在底层看到它
SqlSession是MyBatis的关键对象,是执行持久化操作的独享,类似于JDBC中的Connection.它是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操作的关键对象.SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法,它的底层封装了JDBC连接,可以用SqlSession实例来直接执行被映射的SQL语句.每个线程都应该有它自己的SqlSession实例.SqlSession的实例不能被共享,同时SqlSession也是线程不安全的,绝对不能讲SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中.也绝不能将SqlSession实例的引用放在任何类型的管理范围中,比如Servlet当中的HttpSession对象中.使用完SqlSeesion之后关闭Session很重要,应该确保使用finally块来关闭它.
SqlSession接口源码如下所示:
//SqlSession接口源码如下所示: package org.apache.ibatis.session; import java.io.Closeable; import java.sql.Connection; import java.util.List; import java.util.Map; import org.apache.ibatis.executor.BatchResult; public interface SqlSession extends Closeable { <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); <E> List<E> selectList(String statement); <E> List<E> selectList(String statement, Object parameter); <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds); <K, V> Map<K, V> selectMap(String statement, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey); <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds); void select(String statement, Object parameter, ResultHandler handler); void select(String statement, ResultHandler handler); void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler); int insert(String statement); int insert(String statement, Object parameter); int update(String statement); int update(String statement, Object parameter); int delete(String statement); int delete(String statement, Object parameter); void commit(); void commit(boolean force); void rollback(); void rollback(boolean force); List<BatchResult> flushStatements(); void close(); void clearCache(); Configuration getConfiguration(); <T> T getMapper(Class<T> type); Connection getConnection(); }
那么DefaultSqlSession是怎么进行这些操作的呢?答:通过调用Executor执行器.
Executor执行器.
负责执行SQL语句,并且封装结果集
下面是Executor 的举例
package com.itheima.mybatis.utils; import com.itheima.mybatis.cfg.Mapper; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.List; /** * @author 黑马程序员 * @Company http://www.ithiema.com * 负责执行SQL语句,并且封装结果集 */ public class Executor { public <E> List<E> selectList(Mapper mapper, Connection conn) { PreparedStatement pstm = null; ResultSet rs = null; try { //1.取出mapper中的数据 String queryString = mapper.getQueryString();//select * from user String resultType = mapper.getResultType();//com.itheima.domain.User Class domainClass = Class.forName(resultType); //2.获取PreparedStatement对象 pstm = conn.prepareStatement(queryString); //3.执行SQL语句,获取结果集 rs = pstm.executeQuery(); //4.封装结果集 List<E> list = new ArrayList<E>();//定义返回值 while(rs.next()) { //实例化要封装的实体类对象 E obj = (E)domainClass.newInstance(); //取出结果集的元信息:ResultSetMetaData ResultSetMetaData rsmd = rs.getMetaData(); //取出总列数 int columnCount = rsmd.getColumnCount(); //遍历总列数 for (int i = 1; i <= columnCount; i++) { //获取每列的名称,列名的序号是从1开始的 String columnName = rsmd.getColumnName(i); //根据得到列名,获取每列的值 Object columnValue = rs.getObject(columnName); //给obj赋值:使用Java内省机制(借助PropertyDescriptor实现属性的封装) PropertyDescriptor pd = new PropertyDescriptor(columnName,domainClass);//要求:实体类的属性和数据库表的列名保持一种 //获取它的写入方法 Method writeMethod = pd.getWriteMethod(); //把获取的列的值,给对象赋值 writeMethod.invoke(obj,columnValue); } //把赋好值的对象加入到集合中 list.add(obj); } return list; } catch (Exception e) { throw new RuntimeException(e); } finally { release(pstm,rs); } } private void release(PreparedStatement pstm,ResultSet rs){ if(rs != null){ try { rs.close(); }catch(Exception e){ e.printStackTrace(); } } if(pstm != null){ try { pstm.close(); }catch(Exception e){ e.printStackTrace(); } } } }
所以,咱们平时见到的SqlSession.select()等等其实是在操作DefaultSqlSession.select()的方法,而DefaultSqlSession的方法其实也不是就能够实现对数据库进行操作的,而是通过调用Executor执行器来执行
而Executor执行器其实也是一个接口,而在解析配置文件的时候已经解析出来SQL,那么根据一路传过来的SQL以及参数等信息,Executor再调度StatementHandler等对象对数据库进行增删查改操作.
总结:现在你应该明白了吧,其实我们看到的都是表面,你认为的SqlSession能进行数据库操作其实不是的,是它的实现类再调用底层的Executor,底层的Executor再调度相关数据库操作对象才搞定的,而再要深入它的相关对象,那么就深似海了…
如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。
所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。