目录
一.传统的JDBC连接数据库
流程:
- 注册驱动
- 获取连接创建Statement对象
- execute方法执行SQL
- 把结果集转换成POJO对象
- 关闭资源
缺点:
- 需要手动管理资源
- 代码重复
- 业务逻辑与数据操作的代码耦合
- 结果集需要手动处理
public class JdbcTest {
@Test
public void testJdbc() throws IOException {
Connection conn = null;
Statement stmt = null;
Student student = new Student();
try {
// 注册 JDBC 驱动
// Class.forName("com.mysql.jdbc.Driver");
// 打开连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/springdb", "root", "root");
// 执行查询
stmt = conn.createStatement();
String sql = "SELECT id, name, age clazz_no FROM student where id = 1";
ResultSet rs = stmt.executeQuery(sql);
// 获取结果集
while (rs.next()) {
Integer id = rs.getInt("id");
String name = rs.getString("name");
Integer age = rs.getInt("age");
Integer clazzNo = rs.getInt("clazz_no");
Student.setClazzNo(clazzNo);
Student.setId(id);
Student.setName(name);
Student.setAge(age);
}
System.out.println(student);
rs.close();
stmt.close();
conn.close();
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) stmt.close();
} catch (SQLException se2) {
}
try {
if (conn != null) conn.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
}
二.Mybatis(ORM框架)
1.什么是ORM框架
O:对象 M:映射 R:关系型数据库
数据库中的一条数据对应Java中的一个对象
2.Mybatis特性:
- 使用连接池对连接进行管理
- SQL和代码分离,集中管理
- 参数映射和动态SQL
- 结果集映射
- 缓存管理
- 重复SQL的提取
- 插件机制
三.MyBatis缓存机制
一级缓存和二级缓存在主配置文件中默认都是开启的
<setting name="localCacheScope" value="SESSION"/>
在主配置文件设置一级缓存的作用域 默认就是SESION,如果value设为STATEMENT则相当于关闭了一级缓存
二级缓存有两个开关。总开关在主配置文件中,value改为false则关闭二级缓存
<setting name="cacheEnabled" value="true"/>
第二个开关在mapper.xml中
<cache type="org.apache.ibatis.cache.impl.PerpetualCache" size="1024" eviction="LRU" flushInterval="120000" readOnly="false"/>每个方法也有个控制的开关useCache,flushCache="true"清处二级缓存相应的数据
1.一级缓存
localCache 作用域:session 也就是同一个SqlSession
一级缓存存在的问题:
当我们对数据做修改操作时,这条数据如果在一级缓存中就会被删除。
那么如果有两个SqlSession,一个是sqlSession1另一个是sqlSession2.假如在sqlSession1的一级缓存中已经存在了id=1的缓存数据,然后sqlSession2对id=1的这条数据进行修改,因为每个sqlSession有自己的Executor,所以sqlSession2的修改并不会影响sqlSession1中的缓存,所以sqlSession1的一级缓存中存放的是过时的数据,这时候二级缓存就发挥了作用。
2.二级缓存
作用域:namespace也就是一同个mapper接口
如果开启了二级缓存的功能,就会给我们的Excutor加上装饰CachingExecutor
二级缓存应该工作在一级缓存之前还是之后? 二级先
思考:基于作用范围,应该在什么对象中维护?
只有当事务提交后才会把数据保存到二级缓存,而且相对应的实体类要实现可序列化接口
3.一二级缓存很鸡肋
单机没有问题 但是服务都是分布式的
我们mybatis的缓存放在内存里面(如果没有引入redis的话)
但是引入redis,有点画蛇添足
redis 是db之外的缓存
四.Mybayis插件
MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
插件的工作流程
以pageHelper为例,他会拦截我们的query()方法
他会先生成Executor的代理类,然后拦截query执行代理类的invoke方法,然后会走到pageHelper实现Mybayis的Interceptor实现类(PageInterceptor)的interceptor方法 ,然后会执行PageHelper的分页逻辑,最后再执行method.invoke,也就是被代理类的方法。
多个插件的顺序
取决于我们配置在主配置文件中的插件定义顺序
五.Mybatis执行流程
1.初始化解析配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory factory =
new SqlSessionFactoryBuilder().build(in);
- 会先解析我们的主配置文件,XMLConfigBuild会解析标签封装成一个Configuration对象;
- XMLMapperBuild会解析我的mapper.xml文件,解析mapper节点,得到mapper节点的namespace赋值给currentNamespace
- 把mapper.xml中的sql语句解析然后封装成MapperedStatement(存放解析的select,delete.insert,update)。key是namespqce+id.
- 根据nameSpace拿到接口信息
- mapperRegistry把接口信息注册到knownMappers中,key为mapper接口的class,value为mapperProxyFactory工厂
- 最后new 了一个DefaultSqlSessionFactory工厂
2.创建sqlSession会话
SqlSession sqlSession = factory.openSession();
- 创建事务
- 创建Executor基本类型(SIMPLE/REUSE/BATCH)
- 二级缓存包装CachingExecutor(executor)
- 拦截器包装Executor
- new DefaultSqlSession
3.获取mapper代理对象
StudentDao studentMapper = sqlSession.getMapper(StudentDao.class);
- configuration.getMapper(type, this);
- mapperRegistry.getMapper(type, sqlSession);
- knownMappers.get(type )->获取相应的mapperProxyFactory
- mapperProxyFactory.newInstance 先new 一个mapperProxy。mapperProxy继承了InvocationHandler然后再进行了JDK动态代理
4.调用mapper中的方法
- 会执行mapperProxy对象的invoke方法
- 根据接口以及方法名,获取mappedStatement
- 先找二级缓存
- 没命中再一级缓存
- 在没命中就JDBC执行sql操作