SSMday2——掌握MyBatis的核心对象

一、下面首先介绍Mybatis的核心接口和类。

(1) 每个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心.

(2) 首先获取SqlSessionFactoryBuilder对象,可以根据XML配置文件或者Configuration类的实例构建该对象。

(3) 然后获取SqlSessionFactory对象,该对象实例可以通过SqlSessionFactoryBuilder对象来获取得。

(4) 有了SqlSessionFactory对象之后,就可以进而获取SqlSession实例,SqlSession对象中完全包含以数据库为背景的所有执行SQL操作的方法。就可以用该实例来直接执行已映射的SQL语句。

二、SqlSessionFactoryBuilder

1、SqlSessionBuider的作用

SqlSessionFactoryBuilder负责构建SqlSessionFactory,并且提供了多个build()方法重载.
通过源码分析,可以发现都是在调用同一签名方法:
build(InputStream inputStream,String environment,Properties properties)
由于参数environment和properties都可以为null,那么去除重复的,真正的重载方法其实只有如下三种:
bulid(Configuration config);
build(InputStream inputStream,String environment,Properties properties)
bulid(Reader reader,String enviroment,properties properties)
通过上述分析,发现配置信息可以以三种形式提供给SqlSessionFactoryBuilder的build()方法,分别是InputStream(字节流),Reader(字符流),Configuration(类),由于字节流与字符流都属于读取配置文件的方式,所以从配置信息的来源就很容易想到构建一个SqlSessionFactory有两种方式:读取XML配置文件构建和编程构造方式。我们一般习惯为采取XML配置文件的方式来构造SqlSessionFactory

InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2、SqlSessionFactoryBuider的声明周期和作用域

SqlSessionFactoryBuilder的最大特点是:用过即丢。一旦创建了SqlSessionFactory对象之后,这个类就不再需要存在了,因此SqlSessionFactoryBuilder的最佳范围就是存在于方法体内,也就是局部变量而已。

三、SqlSessionFactory

1、SqlSessionFactory的作用

SqlSessionFactory简单的理解就是创建SqlSession实例的工厂。所有的MyBatis应用都是以SqlSessionFactory实例为中心,SqlSessionFactory的实例可以通过SqlSessionFactoryBuilder对象来获取。有了它以后,顾名思义,就可以通过SqlSession提供的openSession()方法来获取SqlSession实例。而SqlSessionFactoryBuilder则可以通过XML配置文件或一个预先定义好的Configuration实例构建出SqlSessionFactory的实例。

InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2.SqlSessionFactory的生命周期和作用域

SqlSessionFactory对象一旦创建,就会在整个应用程序过程中始终存在。没有理由去销毁或再创建它,并且在应用程序运行中也不建议多次创建SqlSessionFactory。因此SqlSessionFactory的最佳作用域是Application,即随着应用程序的生命周期一直存在。那么这种"存在于整个应用运行期间,并且只存在一个对象实例"的模式就是所谓的单列模式(指在运行期间有且仅有一个实例)。

四、SqlSession

1.SqlSession的作用

SqlSession是用于执行持久化操作的对象,类似于JDBC中的Connection。它提供了面向数据库执行SQL命令所需的所有方法,可以通过SqlSession实例直接运行已映射的SQL语句,其主要作用是执行持久化操作。
使用完SqlSession对象后要及时关闭,通常可以将其放在finally块中关闭。

SqlSession sqlSession = sqlSessionFactory.openSession();
try {
       // 此处执行持久化操作
} finally {
      sqlSession.close();
}

1.1 查询方法:

查询方法:
T selectOne(String statement);
statement是在配置文件中定义的select元素的id。返回执行sql语句查询结果的一条泛型对象。

T selectOne(String statement, Object parameter);

statement是在配置文件中定义的select元素的id。parameter是所需要的参数。返回执行sql语句查询结果的一条泛型对象。
示例

 @Test
    public void findById(){
        SqlSession session = factory.openSession(); //
        Users user = session.selectOne("com.dao.UsersMapper.findById",1);
        session.close();
        // 断言=>true就是断言成功,false就是断言失败
        //assert(user!=null);
        // 断言失败,后续代码不会执行了
        Assert.assertNotNull(user);
        Assert.assertEquals("管理员",user.getNickname());
        Assert.assertEquals("123",user.getPwd());
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        String dateStr = df.format(user.getCreateTime());
        Assert.assertEquals("2019-10-09",dateStr);
    }

List selectList(String statement);

statement是在配置文件中定义的select元素的id。返回执行sql语句查询结果的一条泛型对象的集合

@Test
    public void findAll(){
        SqlSession session = factory.openSession();
        List<Users> list= session.selectList("com.dao.UsersMapper.findAll");
        session.close();
        Assert.assertNotNull(list);
        Assert.assertEquals(3,list.size());
        Assert.assertEquals("管理员",list.get(0).getNickname()); // Ok?
    }

List selectList(String statement, Object parameter);

statement是在配置文件中定义的select元素的id。parameter是所需要的参数。返回执行sql语句查询结果的一条泛型对象的集合.

List selectList(String statement, Object parameter, RowBounds rowBounds);

statement是在配置文件中定义的select元素的id。parameter是所需要的参数。RowBounds是用于分页的参数对象。返回执行sql语句查询结果的一条泛型对象的集合.

void select(String statement, Object parameter, ResultHandler handler);

statement是在配置文件中定义的select元素的id。parameter是所需要的参数。handler对象用于处理查询返回的复杂结果集。

1.2 插入、更新和删除方法

int insert(String statement);
插入方法。参数statement是在配置文件中定义的insert元素的id。使用该方法后,会返回执行sql语句所影响的行数。

int insert(String statement, Object parameter);
插入方法。参数statement是在配置文件中定义的insert元素的id。parameter是插入所需的参数。使用该方法后,会返回执行sql语句所影响的行数。

 @Test
    public void add(){
        Users user = new Users();
        user.setNickname("lisi");
        user.setRealname("李四");
        user.setPhone("1111111");
        user.setEmail("1111111@11.com");
        user.setAddress("洛阳");
        user.setPwd("123");
        user.setCreateTime(new Date()); // jsp->Form->user
        SqlSession session = factory.openSession();
        session.insert("com.dao.UsersMapper.add",user);
        session.commit();  // 提交事务
        session.close();
        session = factory.openSession();
        Users dbUser = session.selectOne("com.dao.UsersMapper.findById",20);
        session.close();
        Assert.assertNotNull(dbUser);
        Assert.assertEquals("李四",dbUser.getRealname());
    }

int update(String statement);

更新方法。参数statement是在配置文件中定义的update元素的id。使用该方法后,会返回执行sql语句所影响的行数。

int update(String statement, Object parameter);

更新方法。参数statement是在配置文件中定义的update元素的id。parameter是更新所需的参数。使用该方法后,会返回执行sql语句所影响的行数。

 @Test
    public void update() throws ParseException {
        // 实际编写DAO层代码的时候,没有这么麻烦
        SqlSession session = factory.openSession();
        Users user = session.selectOne("com.dao.UsersMapper.findById",20);
        user.setNickname("李四");
        // 如果修改时间呢   2020-03-03=>2019-10-01 0:0:0,1000
        /*Calendar instance = Calendar.getInstance(); // 当前时间
        instance.set(Calendar.YEAR,2019);  // 设置年份
        instance.set(Calendar.MONTH,9);   // 月份从0开始,0-11
        instance.set(Calendar.DAY_OF_MONTH,1);
        instance.set(Calendar.HOUR_OF_DAY,0);
        instance.set(Calendar.MINUTE,0);
        instance.set(Calendar.SECOND,0);
        instance.set(Calendar.MILLISECOND,0);
        Date date = instance.getTime();*/
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = df.parse("2019-10-01 02:02:02"); // 页面的表单提交
        user.setCreateTime(date);
        session.update("com.dao.UsersMapper.update",user);
        session.commit(); // 事务提交
        Users dbUser = session.selectOne("com.dao.UsersMapper.findById",20);
        Assert.assertNotNull(dbUser);
        Assert.assertEquals("李四",dbUser.getNickname());
        Assert.assertEquals(date,dbUser.getCreateTime());
        session.close();
    }

int delete(String statement);

删除方法。参数statement是在配置文件中定义的delete元素的id。使用该方法后,会返回执行sql语句所影响的行数。

int delete(String statement, Object parameter);
删除方法。参数statement是在配置文件中定义的delete元素的id。parameter是删除所需的参数.使用该方法后,会返回执行sql语句所影响的行数。

@Test
    public void delete(){
        // 主从表问题?也叫父子表,主键所在的表为主表/父表,外键所在的表为从表/子表
        // 1、添加数据时,先加主表,再加从表
        // 2、删除数据时,先删除从表,在删主表  ,20,
        SqlSession session = factory.openSession();
        Users dbUser = session.selectOne("com.dao.UsersMapper.findById",20);
        Assert.assertNotNull(dbUser);
        session.delete("com.dao.NewsMapper.deleteByUsersId",20);
        session.delete("com.dao.AccessLogsMapper.deleteByUsersId",20);
        session.delete("com.dao.ReplysMapper.deleteByUsersId",20);
        session.delete("com.dao.ShortReplysMapper.deleteByUsersId",20);
        session.delete("com.dao.UsersMapper.delete",20);
        session.commit(); // 事务
        Users user = session.selectOne("com.dao.UsersMapper.findById",20);
        Assert.assertNull(user);
    }

1.3 其他方法:

void commit(); 提交事务的方法。
void rollback(); 回滚事务的方法。
void close(); 关闭SqlSession对象。
T getMapper(Class type); 返回Mapper接口的代理对象。
Connection getConnection(); 获取JDBC数据库连接对象的方法

2.SqlSession生命周期和作用域

正如其名,SqlSession对应着一次数据库会话。由于数据库会话不是永久的,因此SqlSession的生命周期也不应该是永久的。相反,在每次访问数据库时需要创建它(注意:并不是说在SqlSession里执行一次SQL,是完全可以执行多少次的,但是若是关闭了SqlSession,那么就要重新创建它).创建SqlSession的地方只有一个,那就是SqlSessionFactory对象的openSession方法。

需要注意的是:每个线程都有自己的SqlSession实例,SqlSession实例不能被共享,也不是线程安全的。

package com.util;

import com.dao.UsersMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionException;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

/**
 * 1、创建和初始化SqlSessionFactory
 * 2、创建一个open方法返回一定范围内(线程)可用的SqlSession
 * 3、创建一个close方法,来关闭一定范围内的SqlSession
 * 4、创建commit、rollback等事务管理方法
 * 5、创建一些其它的Mybatis相关方法
 */
public class MybatisUtils {

    static ThreadLocal<SqlSession> THREAD_LOCAL = new ThreadLocal<>();
    static final String CONF_FILE = "mybatis-config.xml";
    static SqlSessionFactory factory = null;
    static{
        initFactory(CONF_FILE);
    }
    public static void initFactory(String configFile){
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        try {
            InputStream resourceAsStream =
                    Resources.getResourceAsStream(configFile);
            factory = builder.build(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession open(){
        SqlSession session = THREAD_LOCAL.get();
        if(session == null){
            session = factory.openSession();
            THREAD_LOCAL.set(session);  //
        }
        return session;
    }

    public static void close(){
        SqlSession session = THREAD_LOCAL.get();
        if(session != null){
            session.close();
            THREAD_LOCAL.set(null);
        }
    }

    public static void commit(){
        SqlSession session = THREAD_LOCAL.get();
        if(session!=null)
            session.commit();
    }

    public static void rollback(){
        SqlSession session = THREAD_LOCAL.get();
        if(session!=null)
            session.rollback();
    }

    // 获取Dao层映射对象,
    public static <T> T getMapper(Class<T> tClass){
        return open().getMapper(tClass);
    }

    public static void main(String[] args) {
        SqlSession s1 = open();
        // 1s
        SqlSession s2 = open();
        // 10s
        SqlSession s3 = open();
        // 场景1,重定向
        // s1 -> A(伊滨)   线程1  request->response open()
        // s2 -> B(西工)   线程2  request->response open()
        // s3 -> C(涧西)   线程3  request->response open()
        // s4 -> A(伊滨)   线程4  request->response open()
        // 场景2,转发
        // s <-> A(伊滨) -电话-> B(西工) -(电话)-> C(涧西)
        //                线程1 request->response open()/open()/open()
        UsersMapper dao = getMapper(UsersMapper.class);
    }
}
上一篇:Mybatis其他配置!


下一篇:java maven