一 连接池
1.1 连接池概述
连接池就是用于存储连接的一个容器,用集合对象实现。该集合必须是线程安全的,不能两个线程获取同一个连接。此外,该集合必须实现队列特性,即先进先出。
线程从连接池中获取连接然后访问数据库,访问结束后,将连接还回连接池,而不用断开与数据库的连接。从而减少了访问数据库时获取连接的耗时。
1.2 MyBatis中 的连接池
MyBatis中支持三种连接池的实现方式:POOLED、UNPOOLED、JNDI。主配置文件中dataSource标签的type属性用于指定连接池的实现方式。【与其说是连接池的实现种类,不如说的datasource的种类】
- POOLED:采用传统的javax.sql.DataSource规范的连接池,MyBatis中有基于该规范的实现。
- UNPOOLED:采用每次直接获取连接的方式,虽然实现javax.sql.Datasource接口,但没有池的思想。
- JNDI:采用服务器提供的JNDI技术实现来获取DataSource对象,不同服务器提供的DataSource不同。只能在web或者maven的war工程中使用。tomcat服务器提供的是dbcp连接池。
1.3 配置pooled、unpooled连接池
其实没什么好说的
1.4 配置JNDI连接池
1.4.1 JNDI介绍
JNDI:java命名和目录接口,是SUN公司推出的规范,用于模仿windows系统中的注册表,也就是键值对方式保存和访问值。说的很玄,就是想建立字符串和对象的映射表,通过字符串调用对象。
JNDI要基于Tomcat服务器。Tomcat服务器启动时,会自动创建一些对象,通过为这些对象配置键值对映射,在Java代码中通过键调用Tomcat创建的这些对象。【通过Web应用的配置文件可以让Tomcat在启动时为指定的类创建对象,并配置好键值对映射,然后在Java的配置文件中通过键即可获取对象】
【Java项目中的配置文件在打包后被一起封装到jar包或者war包中了,怎样使用外部的配置文件?】
windows注册表:
JNDI:键为 directory + name,name可自己指定,directory由服务器决定是固定的。
1.4.2 JNDI使用
注意:
1.使用JDNI连接池方式时,是由Tomcat服务器创建和维护连接,Java程序调用连接。所以,Java程序必须要以部署到Tomcat服务器的方式运行。
2.单元测试中的方法始终是手动控制的,main程序部署到外部Tomcat服务器上运行时,test内的方法与Tomcat服务器独立的,无法使用Tomcat服务器提供连接池。
3.JSP是由Tomcat服务器调用的,将Java测试代码放到jsp中可以访问Tomcat提供的数据库连接。JSP中如何使用Java代码?直接使用,具体见下文
创建工程:
-
创建maven项目,选择webapp骨架;
-
创建main/java、main/resources、test、test/java目录;【以上两步是常规的创建maven工程的过程】
-
在webapp目录下创建META-INF目录,创建context.xml配置文件;【也就是创建web应用的配置信息】
context.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<Resource
<!-- name为键中可自己指定的部分-->
name= "jdbc/MyDatasource"
<!-- type为想要创建的对象所属的类-->
type="javax.sql.DataSource"
<!-- auth为对象的创建者,这里Container表示Tomcat-->
auth="Container"
<!-- 最大活动连接-->
maxActive="20"
<!-- 最大等待时长秒数-->
maxWait="10000"
<!-- 最大空闲连接-->
maxIdle="5"
<!-- 数据库的四大参数-->
username="root"
password="123456"
driverClassName="XXX"
url="XXX"
/>
</Context>
Java配置文件(MyBatis主配置文件)
<!-- 配置环境:默认值可任意-->
<environments default="mysql">
<!-- 配置mysql的环境:与上面一致-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type=""></transactionManager>
<!-- 配置数据源:JNDI方式-->
<dataSource type="JNDI">
<!-- java:comp/env/是键中固定的路径,由服务器决定-->
<!-- jdbc/MyDatasource是在context.xml中自定义的-->
<!-- java:comp/env/jdbc/MyDatasource映射到javax.sql.DataSource对象-->
<property name="data_source" value="java:comp/env/jdbc/MyDatasource"/>
</dataSource>
</environment>
</environments>
<%@ page import="java.io.InputStream" %>
<%@ page import="org.apache.ibatis.io.Resources" %>
<%@ page import="org.apache.ibatis.session.SqlSessionFactoryBuilder" %>
<%@ page import="org.apache.ibatis.session.SqlSessionFactory" %>
<%@ page import="org.apache.ibatis.session.SqlSession" %>
<%@ page import="dao.IUserDao" %>
<%@ page import="pojo.User" %>
<%@ page import="java.util.List" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<html>
<body>
<h2>Hello Word</h2>
<%-- 真就直接照搬Java代码,真的就是把Java代码嵌入HML了;JSP中也有导入Java依赖包的操作,而且也能自动导入--%>
<%
// 1. 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 创建SqlSessionFactory工厂类:builder模式
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory =builder.build(in);
// 3. 使用工厂创建SqlSession对象:factory模式
SqlSession sqlSession = factory.openSession();
// 4. 使用SqlSession创建DAO接口的代理对象:代理模式,动态代理
IUserDao userDao = sqlSession.getMapper(IUserDao.class);
// 5. 使用代理对象执行SQL方法
List<User> users = userDao.findAll();
for(User user:users){
System.out.println(user);
}
// 6. 释放资源
sqlSession.close();
in.close();
%>
</body>
</html>
二 延迟加载
略,使用默认策略即可:一对一立即加载,一对多延迟加载
三 缓存
3.1 缓存概述
3.1.1 什么是缓存
存在于内存中的临时数据
3.1.2 为什么使用缓存
查询数据库后将数据保存在内存中,可以减少与数据库的交互次数,提高执行效率
3.1.3 什么样的数据能使用缓存?
特别要考虑:缓存和数据库存在同步的问题,使用缓存就存在不同步的可能性
适用的数据: 经常查询、不经常修改、数据价值小【数据不同步影响不大】
不适用的数据: 不经常查询、经常改变、数据价值大【如:银行汇率】
3.2 MyBatis中的缓存【XML配置实现】
MyBatis中缓存分为一级缓存、二级缓存
3.2.1 一级缓存
- 一级缓存指的是SqlSession对象的缓存。执行查询后,查询结果会同时存入到SqlSession对象维护的一块内存区域,该区域结果为Map。
- 当再次查询同样的数据时,MyBatis会先在该缓存区域中查找,如果存在直接返回。
- 当SqlSession对象消失时,该缓存区域也会消失;SqlSession对象也可以手动清空缓存。
确认和清空缓存:
使用缓存:结果为True,log显示只查询一次
public static void main(String[] args) throws IOException {
// 1. 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 创建SqlSessionFactory工厂类:builder模式
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory =builder.build(in);
// 3. 使用工厂创建SqlSession对象:factory模式
SqlSession session = factory.openSession();
// 4. 使用SqlSession创建DAO接口的代理对象:代理模式,动态代理
IUserDao userDao = session.getMapper(IUserDao.class);
// 5. 使用代理对象执行SQL方法
// 第一查询
List<User> users1 = userDao.findAll();
// 第二次查询
List<User> users2 = userDao.findAll();
// 判断为同一个对象: 输出为True
System.out.println(users1 == users2);
// 6. 释放资源
session.close();
in.close();
}
关闭session后:结果为False,log显示查询两次
...
SqlSession session1 = factory.openSession();
IUserDao userDao1 = session.getMapper(IUserDao.class);
List<User> users1 = userDao.findAll();
session1.close()
SqlSession session2 = factory.openSession();
IUserDao userDao2 = session.getMapper(IUserDao.class);
List<User> users2 = userDao.findAll();
System.out.println(users1 == users2);
清空缓存后:结果为False,log显示查询两次
...
SqlSession session1 = factory.openSession();
IUserDao userDao1 = session.getMapper(IUserDao.class);
List<User> users1 = userDao.findAll();
session1.clearCache()
IUserDao userDao2 = session.getMapper(IUserDao.class);
List<User> users2 = userDao.findAll();
System.out.println(users1 == users2);
缓存同步:这里很扯蛋啊
当调用SqlSession的修改update、添加insert、删除delete操作,
以及commit、close、clearCache操作时,一级缓存都会清空。
这样的话,怎么还会出现缓存和数据库不同步的情况?
更新后判断:False,log显示查询两次
public static void main(String[] args) throws IOException {
// 1. 读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
// 2. 创建SqlSessionFactory工厂类:builder模式
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory =builder.build(in);
// 3. 使用工厂创建SqlSession对象:factory模式
SqlSession session = factory.openSession();
// 4. 使用SqlSession创建DAO接口的代理对象:代理模式,动态代理
IUserDao userDao = session.getMapper(IUserDao.class);
// 5. 使用代理对象执行SQL方法
// 第一次查询
User user1 = userDao.findById(10);
// 更新用户信息
user1.setName("zs");
userDao.updateUser(user1);
// 第二次查询
User user2 = userDao.findById(10);
// 判断为同一个对象
System.out.println(user1 == user2);
// 6. 释放资源
session.close();
in.close();
}
3.2.2 二级缓存
- 二级缓存指的是SqlSessionFactory对象维护的缓存,由同一个SqlSessionFacotry对象创建的SqlSession对象共享同一个二级缓存;
- 二级缓存需要手动开启【一级缓存不需要】;先在主配置文件中开启、然后映射配置文件中开启、最后在Sql语句配置中开启(映射配置文件的select标签中);
- 二级缓存中保存的不是查询结果封装后的POJO对象,而是查询结果的键值对数据,当新的查询访问二级缓存时,再取出封装;
启动二级缓存:
主配置文件:
<settings>
<setting name="cacheEnable" value="true"/>
</settings>
映射配置文件:
<cache></cache>
select标签:
<select id="findAll" resultType="pojo.User" useCache="true">
SELECT * FROM user;
</select>
二级缓存的效果:False,但log显示只查询了一次
...
SqlSession session1 = factory.openSession();
IUserDao userDao1 = session.getMapper(IUserDao.class);
List<User> users1 = userDao.findAll();
session1.close()
SqlSession session2 = factory.openSession();
IUserDao userDao2 = session.getMapper(IUserDao.class);
List<User> users2 = userDao.findAll();
System.out.println(users1 == users2);
3.3 注解配置实现
使用一级缓存
略,一级缓存默认就是开启的,不需要设置
使用二级缓存
主配置文件:
<settings>
<setting name="cacheEnable" value="true"/>
</settings>
Dao接口:
@CacheNamespace直接作用在类上,blocking属性值设为true,表示开启二级缓存【默认值为false】
@CacheNamespace(blocking = true)
public interface IUserDao{
...
}