一、下面首先介绍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);
}
}