1 数据库连接池(数据源)
1.1 概念
数据库连接池, 也可以被称之为数据源(DataSource).
数据库连接池中可以预先创建若干数据连接对象, 然后需要使用的时候, 调用指定的方法即可快速从池中得到数据库连接, 无需再去创建. 提高使用效率, 节省系统开销. 当连接使用完成后, 调用close方法不再关闭连接, 而是将连接归还到连接池.
1.2 常见的数据库连接池
- DBCP, 是Apache下的一款数据库连接池, 很古老了, 很少用了;
- C3P0, 较老了, Hibernate框架;
- Druid, 德鲁伊, 阿里巴巴旗下的数据库连接池, 性能极好;
- HiKariCP, 非常小巧, 性能好的数据库连接池.
2 框架
2.1 什么是框架?
框架是一个半成品, 程序员可在框架的基础上, 添加自己的业务逻辑, 然后完成一个项目. 框使用框架,不是提升代码的执行效率,而是可以简化代码的开发, 提高程序员的工作效率,框架中大量的使用了反射以及各种设计模式. 使用框架很简单, 但是要掌握框架的运行原理就很复杂.
2.2 如何使用框架?
- 找到框架提供的资料(*.jar);
- 项目中引入需要的jar包;
- 提供相应的配置文件(xml, properties, ...);
- 学习框架提供的API.
3 MyBatis
MyBatis的官网: https://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
3.1 MyBatis 环境搭建
3.1.1 创建工程添加依赖
- mybatis-3.5.3.jar MyBatis核心jar包
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
3.1.2 编写MyBatis 核心配置文件
格式要求是xml文件. 一般该配置文件要放在src目录下, 方便加载.
MyBatis实现软编码
可以在核心配置文件中通过<properties>标签配置资源文件的加载, 然后, 在配置数据源时即可通过${}语法读取资源文件中的数据, 从而实现软编码. 利于后续的维护.
在src下,新建File,命名database.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///db_mybatis?useSSL=false
jdbc.username=root
jdbc.password=123456
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--核心配置文件的根元素-->
<configuration>
<!--加载外部properties资源文件-->
<properties resource="database.properties" />
<!--配置系统设置-->
<settings>
<!--开启自动驼峰命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--配置MyBatis的核心运行环境, default用于指定当前生效的是哪个环境-->
<environments default="dev">
<!--用于指定一个环境, 可以出现多个, id用于唯一指定当前环境-->
<environment id="dev">
<!--事务管理器, JDBC表示采用和JDBC一致的方式进行事务管理-->
<transactionManager type="JDBC" />
<!--数据源, POOLED表示采用数据库连接池获取数据库连接-->
<dataSource type="POOLED">
<!--连接数据库的参数-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--扫描映射文件-->
<mappers>
<mapper resource="UserMapper.xml" />
</mappers>
</configuration>
- <configuration>, 核心配置文件中的根元素, 所有的其他标签都要定义在该标签下;
- <environments>, 用于管理多个环境配置;
- default属性, 用于指定当前使用的是哪个环境, 对应<environment>的id属性;
- <environment>, 用于配置一个运行环境;
- id属性, 用于唯一标识当前环境;
- <transactionManager>, 配置事务管理器;
- type属性, 用于指定使用哪种方式进行事务管理:
- JDBC: 表示MyBatis使用JDBC的方式进行事务管理;
- MANAGED: 表示MyBatis不进行事务管理, 交由第三方容器管理;
- type属性, 用于指定使用哪种方式进行事务管理:
- <dataSource>, 配置数据源;
- type属性, 用于指定使用哪种数据源;
- UNPOOLED: 不采用数据库连接池, 每次进行连接的创建和关闭;
- POOLED: 采用MyBatis默认的数据库连接池;
- JNDI: java命名和目录接口, 表示Mybatis不进行数据源的管理, 交由第三方容器负责.
- type属性, 用于指定使用哪种数据源;
3.2 准备要操作的数据库和实体类
create table tb_user (
id int primary key auto_increment,
username varchar(30) not null unique,
password char(32) not null,
real_name varchar(20),
age int(3),
birthday date,
reg_time datetime
);
insert into tb_user values
(default, 'zhangsan', '123', '张三', 18, '2001-02-12', now()),
(default, 'lisi', '123', '李四', 19, '2000-12-14', now()),
(default, 'wangwu', '123', '王五', 20, '1999-11-11', now());
public class User {
private Integer id;
private String username;
private String password;
private String realName;
private Integer age;
private Date birthday;
private Date regTime;
//构造方法(有参、无参)
//getter、setter
//toString()
}
3.3 编写映射文件
映射配置文件主要用于定义要执行的SQL语句, 同时声明数据库表格和对象之间的映射关系. 要求也是一个xml文件, 命名和位置没有要求. 为了加载方便, 一般也可以直接放在src目录下.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace属性表示命名空间, 类似于java中的package-->
<mapper namespace="a.b.c">
<!--
select标签用于指定查询的SQL语句
id属性用于唯一标识一条SQL语句, 类似于之前代码方法名
resultType属性用于告诉MyBatis要将查询结果封装成什么对象
-->
<select id="selAll" resultType="com.wuyw.pojo.User">
select * from tb_user
</select>
</mapper>
注意: namespace属性必须定义, 否则报错!
3.4 在核心配置文件中进行映射文件扫描
<!--扫描映射文件-->
<mappers>
<mapper resource="UserMapper.xml" />
</mappers>
<!-- 通过package统一扫描映射-->
<!--要求映射文件和接口同名, 并存放在相同的路径下-->
<mappers>
<!--package的使用要求遵循两个可选规范-->
<package name="com.wuyw.mapper" />
</mappers>
<mappers>标签要定义在<environments>标签后. <mapper>有三个属性:
- resource: 用于加载当前项目下的映射文件;
- url: 用于加载远程的资源文件;
- class: 用于加载java类.
3.5 MyBatis接口绑定
MyBatis中的接口绑定方案, 指的是将一个用户自定义的接口和一个对应的映射配置文件进行绑定.只需要提供接口及抽象方法, 同时将对应要执行的SQL语句写到映射文件中, 然后, MyBatis会将接口和映射文件进行绑定, 相当于接口的实现类交给MyBatis完成了. 要想实现接口绑定,
3.5.1 接口绑定规范
1 必要规范
- namespace的值必须和对应接口的全限定路径一致;
- SQL语句标签的id属性必须和当前绑定的接口中对用方法的名称一致;
2 可选规范
- 接口的名称和映射文件的名称一致;
- 将接口和映射文件放在相同的位置;
public interface UserMapper {
/**
* 查询所有用户信息
*
*/
List<User> selAll();
}
public class TestBind {
@Test
public void testSelAll() {
SqlSession session = MyBatisUtil.getSession();
// 获取接口类型对象的方法: getMapper
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.selAll();
session.close();
}
}
<mapper namespace="com.wuyw.mapper.UserMapper">
<!--查询所有, 无参-->
<select id="selAll" resultType="user">
select * from tb_user
</select>
</mapper>
3.5.2 接口绑定中的参数传递
- 接口绑定中, 支持多种参数传递方式;
- 简单类型, Map类型和Pojo类型跟之前一样;链接地址: https://blog.csdn.net/wuyanwenyun/article/details/105820912
- 多参数传参:
- 不使用注解, 可以通过arg(0开始)或param(1开始)形式获取;
- 使用@Param(key)注解时, 可以通过key或param形式获取.
public interface UserMapper {
/**
* 查询所有用户信息
*/
List<User> selAll();
/**
* 根据id查询用户
*/
User selById(int id);
/**
* map传参查询
*/
List<User> selByMap(Map<String, Object> params);
/**
* 对象传参查询
*/
List<User> selByPojo(User user);
/**
* 登录查询
*/
User sel4Login(@Param("uname") String username, @Param("pwd") String password);
}
<mapper namespace="com.wuyw.mapper.UserMapper">
<!--查询所有, 无参-->
<select id="selAll" resultType="user">
select * from tb_user
</select>
<!--根据id查, 简单参数传递-->
<select id="selById" resultType="user">
select * from tb_user where id=#{suibian}
</select>
<!--条件查询, Map传参-->
<select id="selByMap" resultType="user">
select * from tb_user where username=#{uname} or age=#{age}
</select>
<!--条件查询, 对象传参-->
<select id="selByPojo" resultType="user">
select * from tb_user where real_name=#{realName}
</select>
<!--登录查询, 多条件传参-->
<select id="sel4Login" resultType="user">
select * from tb_user where username=#{uname} and password=#{pwd}
</select>
</mapper>
3.6 提取 MyBatis 连接的工具类
package com.wuyw.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory factory;
static {
try {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
throw new RuntimeException("工厂创建失败!");
}
}
public static SqlSession getSession() {
return factory.openSession();
}
}
4 MyBatis 中的缓存
缓存是内存中的一块区域, 用来存放一些数据, 从而提高查询的效率. MyBatis中支持数据缓存, 分为两个级别: 一级缓存和二级缓存. 一级缓存默认开启, 二级缓存默认关闭.
4.1 一级缓存
本地会话缓存, 也被称之为一级缓存, 默认开启. 指的是在同一个SqlSession中,调用的方法 和传入的参数一致时 多次查询,减少查询次数,容易造成数据脏读
同一个SQLSession,同一个参数
调用的方法 SQL语句的名称空间和id相同
SqlSession内部的缓存如何实现?
- Map集合实现缓存
- 键 推测: 名称空间+SQLid+参数+SQLSession.hashcode
- 值 SQL语句查询的结果
脏读问题怎么解决?
一级缓存默认开启 无法关闭
手动刷新 清空缓存 sqlSession.clearCache();
发生了增删改 之后的提交动作 也会默认清空缓存
4.2 二级缓存
MyBatis基于SqlSessionFactory级别做的缓存. 二级缓存默认是关闭的, 如果要使用, 需要在指定的命名空间通过配置进行开启. 使用<cache />即可.
一级缓存作用于一个SqlSession
二级缓存作用于一个namespace
以名称空间作为缓存的标志 每一名称空间都有自己独立的缓存区域
多个mapper对象可以是同一个名称空间, 二级缓存可以跨mapper对象,必须同一个接口的mapper对象
多个SQLSession对象,可以使用同一个名称空间, 二级缓存可以跨SQLSession对象
在项目中 SQLsession对象应该避免大量创建
同一个接口下 的mapper代理对象应该避免大量创建 所以二级缓存用的不多
因为用的不多 二级缓存不是自动开启的
<!-- 开启二级缓存 -->
<!-- 实体类 要实现序列化接口 -->
<!--
方式一
核心配置文件中开启二级缓存 总开关
-->
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
<!--
方式二
Mapper映射文件中 开启针对于某个名称空间的二级缓存
-->
<mapper namespace="com.wuyw.mapper.UserMapper">
<cache/>
<!--
方式三
针对于SQL语句是否使用二级缓存的设置
useCache控制当前SQL语句是否使用二级缓存默认值为true默认开启
flushache控制当前SQL语句执行一次之后是否会刷新缓存默认为false
-->
<select id="selById" resultType="user" useCache="true" flushache="false" >
select * from tb_user where id=#{suibian}
</select>
<!--
eviction: 回收策略.默认的是 LRU
LRU: 最近最少使用的:移除最长时间不被使用的对象
FIFO: 先进先出,按对象进入缓存的顺序来移除它们
flushInterval: 刷新间隔, 默认不刷新, 缓存仅仅调用语句时刷新,单位是毫秒
readOnly: 是否是只读
true: 表示只读
false: 默认值, 表示可读可写, 要求实体类可序列化
size: 记录数, 默认为1024
type: 表示自定义缓存使用的全限定路径, 一般用于第三方缓存方案
-->
<cache
eviction="LRU"
flushInterval="60000"
readOnly="false"
size="1024" />