共计 19870 个字符,预计需要花费 50 分钟才能阅读完成。
一、什么是 Mybatis
这里借用官网的一句话介绍什么是 mybatis:MyBatis 是一款优良的长久层框架,它反对定制化 SQL、存储过程以及高级映射。MyBatis 防止了简直所有的 JDBC 代码和手动设置参数以及获取后果集。MyBatis 能够应用简略的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects, 一般的 Java 对象)映射成数据库中的记录。
二、Mybatis 绝对 JDBC 有哪些劣势
首先咱们来看一看 jdbc 连贯数据库的连贯办法:
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
//1、加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
//2、通过驱动治理类获取数据库链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "root");
//3、定义 sql 语句 ? 示意占位符
String sql = "select * from user where username = ?";
//4、获取预处理 statement
preparedStatement = connection.prepareStatement(sql);
//5、设置参数,第一个参数为 sql 语句中参数的序号(从 1 开始),第二个参数为设置的参数值
preparedStatement.setString(1, "王五");
//6、向数据库收回 sql 执行查问,查问出后果集
resultSet = preparedStatement.executeQuery();
//7、遍历查问后果集
while(resultSet.next()){
User user
System.out.println(resultSet.getString("id")+""+resultSet.getString("username"));
}
} catch (Exception e) {e.printStackTrace();
}finally{
//8、开释资源
if(resultSet!=null){
try {resultSet.close();// 开释后果集
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();}
}
if(preparedStatement!=null){
try {preparedStatement.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();}
}
if(connection!=null){
try {connection.close();// 敞开数据库连贯
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();}
}
}
}
通过下面的一段 jdbc 连贯数据代码,咱们看有哪些不好的中央:
1. 在创立 connection 的时候,存在硬编码问题(也就是间接把连贯信息写死,不不便前期保护)
2.preparedStatement 对象在执行 sql 语句的时候存在硬编码问题。
3. 每次在进行一次数据库连贯后都会敞开数据库连贯,频繁的开启 / 敞开数据连贯影响性能。
简略的说一下 mybatis 绝对 jdbc 的劣势:
1.mybatis 是把连贯数据库的信息都是写在配置文件中,因而不存在硬编码问题,不便前期保护。
2.mybatis 执行的 sql 语句都是通过配置文件进行配置,不须要写在 java 代码中。
3.mybatis 的连接池治理、缓存治理等让连贯数据库和查问数据效率更高。
三、Mybatis 框架的原理介绍
这里就通过一张图来对 mybatis 框架原理进行介绍吧:
四、Mybatis 全局配置文件
SqlMapConfig.xml 是 Mybatis 的全局配置文件,它的名称能够是任意,然而个别命名都为(SqlMapConfig)
4.1. 全局配置文件的类容和程序
Properties(属性)
Settings(全局参数设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境信息汇合)
environment(单个环境信息)
transactionManager(事物)
dataSource(数据源)
mappers(映射器)
4.2. 常见配置详解
properties 标签:
Mybatis 能够通过该标签来读取 java 配置信息:
例如在工程中对数据源信息写在 db.properties 文件中,能够通过 properties 标签来加载该文件。
db.properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/mybatis
db.username=root
db.password=12345678
SqlMapConfig.xml 应用 properties 标签:
<!-- 通过 properties 标签,读取 java 配置文件的内容 -->
<properties resource="db.properties" />
<!-- 配置 mybatis 的环境信息 -->
<environments default="development">
<environment id="development">
<!-- 配置 JDBC 事务管制,由 mybatis 进行治理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采纳 dbcp 连接池 -->
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
留神:
1、先加载 properties 中 property 标签申明的属性
因而在 property 中的 name 属性的值和 value 比 properties 中的 resource 属性先加载。后加载的 db.properties 会笼罩于 property 加载属性和值。
<properties resource="db.properties">
<property name="db.username",value="1234"/>
</properties>
2、再加载 properties 标签引入的 java 配置文件中的属性
3、parameterType 的值会和 properties 的属性值发生冲突。因而,在 properties 文件里的内容命名最好加上 db. 代表是跟数据源相干的属性,这样就不容易跟当前的属性发生冲突。
settings 标签:
该标签时 mybatis 的全局设置,该设置会影响 mybatis 的运行。
个别咱们应用应用该标签来开启二级缓存和懒加载。
以下是几张 settings 配置项的阐明:
typeAliases 标签
该标签是对 po 类进行别名设置,这样,在前面应用 po 类的时候就能够间接通过别名援用,而不须要通过 po 类的全限定名来援用。这样能够进步咱们的开发效率。
首先介绍下 Mybatis 的默认提供的别名有:
自定义单个别名:这种形式只能定义单个类的别名。
上面的代码就是把 com.lc.mybatis.po.User 类定义为 user 的别名
<typeAliases>
<!-- 设置单个别名 -->
<typeAlias type="com.lc.mybatis.po.User" alias="user"/>
</typeAliases>
自定义之批量定义别名:
上面代码是把 com.lc.mybatis.po 类下的所有类都申明别名,默认的别名就是类名(类名大小写都能够)
<!-- 设置别名 -->
<typeAliases>
<!-- 批量设置别名 -->
<!-- package: 指定包名称来为该包下的 po 类申明别名,默认的别名就是类名(类名首字母大小写都能够)-->
<package name="com.lc.mybatis.po"/>
</typeAliases>
mappers 标签
该标签的作用是加载映射文件
形式一:<mapper resource=””/>
该形式是加载绝对于类门路下的映射文件:
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
形式二:<mapper url=””/>
该形式应用全限定门路
<mapper url="file:///D:\workspace_spingmvc\mybatis_01\config\sqlmap\User.xml" />
形式三:<mapper class=””/>
该形式应用 mapper 接口的全限定类名
<mapper class="cn.itcast.lc.mapper.UserMapper"/>
此形式要求:
Mapper 接口 Mapper 映射文件名称雷同且在同一个目录下。
形式四:<package name=””/>
该形式是加载指定包下的所有映射文件
<package name="cn.lc.mybatis.mapper"/>
此形式要求:
Mapper 接口 Mapper 映射文件名称雷同且在同一个目录下。
五、映射文件
5.1. 输出映射 parameterType
第一种:简略类型
{}示意占位符?,parameterType 接管简略类型的参数时,外面的名称能够任意
<select id="findUserById" parameterType="java.lang.Integer" resultType="user">
SELECT * FROM USER WHERE id = #{id}
</select>
第二种:pojo 类型
这里通过用户的用户名进行含糊查问演示 pojo 类型
在映射文件中增加含糊查问语句:
<!– parameterType 传递 pojo 类型 –>
<select id="findUsersByPojo" parameterType="com.lc.mybatis.po.User" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE "%${username}%"
</select>
user 类
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
}
测试类:
public class UserDao{
// 依据用户名进行含糊查问
@Override
public List<User> findUserByPojo(){
// 全局配置文件门路:String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创立 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setUsetname("张三");
List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo",user);// 传入 pojo
System.out.println(users);
sqlSession.close();
return users;
}
}
第三种:包装类型 pojo
这里通过用户名和用户地址对用户进行查问来演示包装类型 pojo:
首先创立包装 pojo 类:
public class UserVO {
private User user;
public User getUser() {return user;}
public void setUser(User user) {this.user = user;}
}
在映射文件中增加查问语句
<!-- parameterType 传递 pojo 包装类型 -->
<select id="findUsersByPojo1" parameterType="com.lc.mybatis.po.UserVO" resultType="user">
SELECT * FROM USER WHERE username LIKE "%${user.username}%" AND address=#{user.address}
</select>
测试类
public class UserDao{
// 依据用户名和地址进行查问
@Override
public List<User> findUserByPojo1(){
// 全局配置文件门路:String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创立 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setUsetname("张三");
user.setAddress("郝汉山");
UserVO userVo = new UserVO();
userVo.setUser(user);
List<User> users = (List<User>)sqlSession.selectList("test.findUsersByPojo1",userVo);// 传入 pojo 包装类
System.out.println(users);
return users;
}
}
第四种:map 汇合类型
这里通过查问用户信息演示:
在映射文件中增加该查问语句
<!-- parameterType 传递 hashmap 类型 -->
<select id="findUsersByMap" parameterType="java.util.Map" resultType="user">
SELECT * FROM USER WHERE username LIKE "%${username}%" AND address=#{address}
</select>
测试方法:
public class UserDao{
// 依据用户名和地址进行查问
@Override
public List<User> findUserByMap(){
// 全局配置文件门路:String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创立 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
Map<String,String> map = new HashMap<>();
map.put("username","张三");
map.put("address","郝汉山");
List<User> users = (List<User>) sqlSession.selectList("test.findUsersByMap",map);// 传入 pojo 包装类
System.out.println(users);
sqlSession.close();
return users;
}
}
5.2.resultType 后果映射
resultType 后果映射要求:须要查问后果的列名和映射的对象的属性名统一,这样能力映射胜利。如果映射没胜利也不会报错,只是映射后果中对象的相应属性没有值,为空。如果映射的列名和对象中的属性名全副不统一,那么映射的对象为空。如果在应用 sql 语句查问的时候给查问后果列设置了别名,则别名要和映射后果对象的属性名统一,这样能力保障映射胜利。
第一种:简略类型
留神:如果后果映射为简略类型,则须要查问的后果为一列能力映射胜利。
例如:查问用户表中用户的总数。
映射文件为:
<!-- resultType: 输入为简略类型 -->
<select id="findUserCount" resultType="int">
select count(*) from user;
</select>
测试代码:
public class UserDao{
@Override
public int findUserCount(){
// 全局配置文件门路:String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创立 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
int userCount= sqlSession.selectOne("test.findUserCount");
System.out.println(userCount);
sqlSession.close();
return userCount;
}
}
第二种:pojo 后果映射
这里操作 pojo 输出映射。
5.3.resultMap 后果映射
应用 resultMap 后果映射时,不须要查问进去的后果集的列名和映射后果对象的属性名雷同,然而须要申明一个 resultMap,手动的形式来对列名和对象属性进行映射。(resultMap 个别用于多表关联映射)
例如:通过查问用户表中的用户,并对查问进去的用户表的列名设置别名。
映射文件增加查问语句:
[id]:定义 resultMap 的惟一标识
[type]:定义该 resultMap 最终映射的 pojo 对象
[id 标签]:映射后果集的惟一标识列,如果是多个字段联结惟一,则定义多个 id 标签
[result 标签]:映射后果集的一般列
[column]:SQL 查问的列名,如果列有别名,则该处填写别名
[property]:pojo 对象的属性名
<!-- 如果查问进去的列名有别名就不能通过 resultType 来接管输入类型了。须要通过 resultMap 来申明传出类型(resultMap 须要申明)-->
<resultMap type="user" id="userMap">
<!-- id 标签:专门查问后果中惟一列映射 -->
<id column="id_" property="id"/>
<!-- result 标签:映射查问后果中的一般列 -->
<result column="username_" property="username"/>
<result column="address_" property="address"/>
</resultMap>
<select id="findUserResultMap" parameterType="int" resultMap="userMap">
select id id_,username username_,address address_ from user where id=#{id}
</select>
测试类:
public class UserDao{
@Override
public User findUserResultMap(){
// 全局配置文件门路:String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创立 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserResultMap",1);
System.out.println(user);
sqlSession.close();
return user;
}
}
5.4. 动静 sql
在 mybatis 中提供了一些动静 sql 标签,能够让咱们开发效率更快,这些动静 sql 语句能够减少咱们写的 sql 语句的重用性,罕用的动静 sql 语句标签有:if 标签、sql 片段(须要先定义后应用)、where 标签、foreach 标签
通过上面例子来演示动静 sql 罕用的标签:
需要剖析:查问用户表中相干的用户。
收回相干的 sq 语句有:
select * from user where username like "% 张三 %" and address= ?;
select * from user;
select * from user where id in(1,2,10);
如何能力让这些语句在咱们须要的时候就应用,不须要的时候就不实用。其实咱们发现这三条语句有很多反复的中央,咱们如果能做到让反复的可能重复使用,没有反复的能够按咱们需要应用的话就能缩小咱们写很多 sql 语句。当然动静 sql 就是帮咱们解决这些问题的。
映射文件:
<!-- 定义 sql 片段,用于可重用的 sql 片段 -->
<sql id="whereClause">
<!-- if 标签:能够对输出的参数进行判断 -->
<!-- test: 指定判断表达式 -->
<if test="user!=null">
<if test="user.username!=null and user.username!=''">
AND username LIKE '%${user.username}%'
</if>
<if test="user.address!=null and user.address!=''">
AND address=#{user.address}
</if>
</if>
<if test="idList!=null">
AND id IN
<!-- foreach 标签:能够循环传入参数值 -->
<!-- collenction:标示 pojo 中汇合属性的属性名称 -->
<!-- item: 每次遍历进去的对象 -->
<!--open 开始遍历时拼接的串 -->
<!--close 完结遍历时拼接的串 -->
<!--separator 遍历每个对象之间须要拼接字符 -->
<foreach collection="idList" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</if>
</sql>
<select id="findUserList" parameterType="userVO" resultType="user">
SELECT * FROM USER
<!-- where 标签:默认去掉第一个 AND,如果没有参数就去掉 where 本人 -->
<where>
<!-- 援用 sql 语句片段 -->
<include refid="whereClause"></include>
</where>
</select>
userVo 类:
public class UserVO {
private User user;
private List<Integer> idList;
public List<Integer> getIdList() {return idList;}
public void setIdList(List<Integer> idList) {this.idList = idList;}
public User getUser() {return user;}
public void setUser(User user) {this.user = user;}
}
测试类:
public class UserDao{
@Override
public void findUserList(){
// 全局配置文件门路:String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 创立 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 测试 foreach 语句执行
UserVO userVo = new UserVO();
List<Integer> idList = new ArrayList<>();
idList.add(1);
idList.add(2);
dList.add(3);
userVo.setIdList(idList);
// 测试 select * from user where username like ? and address=?;
//User user = new User();
//user.setUsername("张三");
//user.setAddress("武当山");
//userVo.setUser(user);
List<User> users = sqlSession.selectOne("test.findUserList",userVo);
System.out.println(users);
sqlSession.close();}
}
六、Mybatis 之传统 Dao 层的开发(该形式很少用)
6.1. 开发环境筹备:
1. 集体应用的 jdk 为 1.7
2. 开发工具是应用的 Eclipse4.5
3. 数据库应用的 MySQL5X
4. 导入 mybatis 所须要的 jar 包
5. 导入 mysql 数据库驱动包
6.2. 需要剖析:
依据 ID 查问查问用户、依据用户名称进行含糊查问、增加用户
6.3. 代码实现
创立用户表对应 pojo 类 User:
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//setter 和 get 办法省略
}
创立 mybatis 的全局配置文件 SqlMapConfig.xml:用于配置数据源、事务、映射文件加载等信息
<?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>
<!-- 配置 mybatis 的环境信息 -->
<environments default="developments">
<environment id="developments">
<!-- 配置 JDBC 事务管制,由 mybatis 进行治理 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源,采纳 mybatis 连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="12345678"/>
</dataSource>
</environment>
</environments>
<!-- 加载映射文件 -->
<mappers>
<mapper resource="User.xml"/>
</mappers>
</configuration>
创立需要开发的映射文件 User.xml 配置文件:并在全局配置文件中增加该映射文件
<?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:命名空间,它的作用就是对 SQL 进行分类化治理,能够了解为 SQL 隔离
留神:应用 mapper 代理开发时,namespace 有非凡且重要的作用
-->
<mapper namespace="test">
<!-- 依据用户 ID 查问用户信息 -->
<!-- select: 示意一个 MappedStatement 对象 -->
<!-- id:statement 的惟一标识 -->
<!-- #{}: 示意一个占位符?-->
<!-- #{id}: 外面的 id 示意输出参数的参数名称,如果该参数为简略类型,那么 #{}外面的参数能够任意 -->
<!-- parameterType: 输出参数的 java 类型 -->
<!-- resultType: 输入后果的所映射的 java 类型(单条后果所对应的 java 类型) -->
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE id = #{id}
</select>
<!-- 依据用户名进行含糊查问 -->
<!-- ${}: 示意一个 sql 连接符 -->
<!-- ${value}: 外面的 value 示意输出的名称,如果该参数是简略类型,那么 ${}外面的参数名称必须是 value-->
<!-- ${}这种写法存在 sql 注入的危险,所以须要 慎用!然而有些场景须要用到比方:排序,-->
<select id="findUsersByName" parameterType="java.lang.String" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE username LIKE "%${value}%"
</select>
<!-- 增加用户:-->
<!--
[selectKey 标签]:通过 select 查问来生成主键
[keyProperty]:指定寄存生成主键的属性
[resultType]:生成主键所对应的 Java 类型
[order]:指定该查问主键 SQL 语句的执行程序,绝对于 insert 语句
[last_insert_id]:MySQL 的函数,要配合 insert 语句一起应用, 该函数在 mysql 中是执行在 insert 语句后。-->
<insert id="insertUser" parameterType="com.lc.mybatis.po.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO USER(username,sex,birthday,address) VALUES(#{username},#{sex},#{birthday},#{address})
</insert>
</mapper>
dao 层接口:
public interface UserDao {
// 依据用户 id 查问用户
public User findUserById(int id);
// 依据用户名进行含糊查问
public List<User> findUsersByName(String name);
// 增加用户
public void addUser(User user);
}
dao 层的实现:
public class UserDaoImpl implements UserDao{
// 通过构造方法注入 sqlSessionFactory
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {this.sqlSessionFactory = sqlSessionFactory;}
// 依据 id 查问用户
@Override
public User findUserById(int id) {SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
System.out.println(user);
sqlSession.close();
return user;
}
// 依据用户名进行含糊查问
@Override
public List<User> findUsersByName(String name){SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> users = sqlSession.selectList("test.findUsersByName",name);
System.out.println(users);
sqlSession.close();
return users;
}
// 增加用户
public List<User> addUser(String name){SqlSession sqlSession = sqlSessionFactory.openSession();
User user = new User();
user.setUsername("超哥 2");
user.setAddress("成都市");
// 调用 SqlSession 的增删改查办法
// 第一个参数:示意 statement 的惟一示意
// 第一个参数:示意占位符的
sqlSession.insert("test.insertUser", user);
System.out.println(user.getId());
// 切记:增删改操作须要提交事务。sqlSession.commit();
// 敞开资源
sqlSession.close();}
测试类:这里就只通过依据 id 查找用户进行测试
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void setUp() throws IOException {InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {UserDao userDao = new UserDaoImpl(sqlSessionFactory);
userDao.findUserById(1);
}
}
七、Mybatis 之 Mapper 接口的开发方式
该形式开发,不须要写 dao 层的实现类,而是 mybatis 依据映射文件等信息对接口进行 jdk 动静代理生成代理类来实现接口中的办法,因而,采纳这种形式,咱们只须要编辑接口,而不须要去写实现。
7.1. 需要剖析
依据 id 查问用户。
7.2.Mapper 开发代理标准
1、mapper 接口的全限定名要和 mapper 映射文件的 namespace 值统一。
2、mapper 接口的办法名称要和 mapper 映射文件的 statement 的 id 统一。
3、mapper 接口的办法参数类型要和 mapper 映射文件的 statement 的 parameterType 的值统一,而且它的参数是一个。
4、mapper 接口的办法返回值类型要和 mapper 映射文件的 statement 的 resultType 的值统一。
7.3. 代码实现
筹备 po 类:
public class User {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
//getter 和 setter 办法省略
}
Mapper 接口:
public interface UserMapper {public User findUserById(int id);
}
UserMapper.xml 配置文件:<?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:命名空间,它的作用就是对 SQL 进行分类化治理,能够了解为 SQL 隔离
留神:应用 mapper 代理开发时,namespace 有非凡且重要的作用
-->
<mapper namespace="com.lc.mybatis.mapper.UserMapper">
<!-- 依据用户 ID 查问用户信息 -->
<!-- select: 示意一个 MappedStatement 对象 -->
<!-- id:statement 的惟一标识 -->
<!-- #{}: 示意一个占位符?-->
<!-- #{id}: 外面的 id 示意输出参数的参数名称,如果该参数为简略类型,那么 #{}外面的参数能够任意 -->
<!-- parameterType: 输出参数的 java 类型 -->
<!-- resultType: 输入后果的所映射的 java 类型(单条后果所对应的 java 类型) -->
<select id="findUserById" parameterType="java.lang.Integer" resultType="com.lc.mybatis.po.User">
SELECT * FROM USER WHERE id = #{id}
</select>
</mapper>
在全局配置文件 SqlMapperConfig 中增加该映射文件
测试代码:
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory ;
@Before
public void setUp() throws IOException {InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindUserById() {SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取 UserMapper 的代理类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.findUserById(10);
System.out.println(user);
sqlSession.close();}
}
八、Mybatis 一级缓存
mybatis 提供查问缓存,如果缓存中有数据,就不必从数据库中获取,用于加重数据压力,进步零碎性能
一级缓存是 sqlSession 级别的缓存,在操作数据库的时候,须要结构 sqlSession 对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的 SqlSession 的缓存区域(HashMap)是相互不受影响的。
mybatis 默认是反对一级缓存的。
8.1. 证实一级缓存的存在:
验证办法:
public void testOneLevelCache() {SqlSession sqlSession = sqlSessionFactory.openSession();
// 获取 UserMapper 的代理类
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 第一次查问
User user = userMapper.findUserById(10);
System.out.println(user);
// 第二次查问
User user2 = userMapper.findUserById(10);
System.out.println(user2);
sqlSes
通过执行该办法,查看打印日志能够看出就执行了一次 sql 语句的查问,因而能够判断第二次查问是没有从数据库中进行查问的。
那当什么时候一级缓存会被清空呢?
通过测试发现,当在第二次查询数据库之前对数据库进行了写的操作后,第二次查问就不会从缓存中查问后果,而是间接从数据中查问后果。另一种就是在第一查问后果后,手动的形式清空一级缓存(应用 sqlSession.clearCache(); 办法。)
测试方法:
九、Mybatis 二级缓存
二级缓存是 Mapper 级别的缓存。多个 SqlSession 去操作同一个 Mapper 的 sql 语句,多个 SqlSession 能够共用二级缓存,二级缓存是夸 SqlSession 的。(二级缓存 Mybatis 默认是敞开的,须要本人去手动配置开启或能够本人抉择用哪个厂家的缓存来作为二级缓存)
9.1. 开启二级缓存
首先在全局配置文件中配置开启二级缓存性能:
<!-- 开启二级缓存总开关 -->
<setting name="cacheEnabled" value="true"/>
在映射文件中去抉择是否该映射文件应用二级缓存:
如果应用就进行以下配置,如果不是用,就不须要对映射文件做任何批改。
<!-- 开启本 mapper 下的 namespace 的二级缓存,默认应用的是 mybatis 提供的 PerpetualCache -->
<cache></cache>
也能够设置抉择二级缓存提工商
以下是我抉择应用 EhcacheCache 缓存(留神:在应用 EhcacheCache 缓存须要导入 jar 包)
最初就是,在须要缓存的 po 类中去实现序列化:
验证二级缓存是否开启:
验证办法如下:
@Test
// 测试二级缓存是否开启
public void testTwoLevelCache() {SqlSession sqlSession1 = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
SqlSession sqlSession3 = sqlSessionFactory.openSession();
// 获取 UserMapper 的代理类
UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
User user1 = userMapper1.findUserById(10);
System.out.println(user1);
// 当 sqlSession1 执行 close()的时候,才把 sqlSession1 查问的后果写到二级缓存中去。sqlSession1.close();
// User u = new User();
// u.setUsername("小胖");
// u.setAddress("成都");
// u.setSex("2");
// u.setBirthday(new Date());
// userMapper3.insertUser(u);
// // 当其中任何一个 sqlSession 执行 commit()办法后将刷新整个缓存区
// sqlSession3.commit();
// sqlSession3.close();
// 第二次查问
User user2 = userMapper2.findUserById(10);
System.out.println(user2);
sqlSession2.close();}
测试后果:
同时通过测试,当在第二次查问的时候,向数据库提交数据后,就会对缓存进行刷新,这样在第二次查问的时候就没有走缓存,而是走的数据库。
9.2. 禁用二级缓存
因为二级缓存默认是敞开的,如果不开启就不会应用二级缓存。如果,咱们开启了二级缓存,而在有些查问后果集中,不须要受到二级缓存影响,该怎么去做呢?
在 select 查问中,默认是应用了二级缓存,如果不想应用二级缓存,就在 select 标签中有一个 useCache 的属性设置为 false,就代表不应用二级缓存,每次进行查问数据都不会从缓存总获取,而是间接从数据库中进行查问。useCache 的默认值是 true,即代表 statement 应用二级缓存。
9.3. 刷新二级缓存
在 statement 中设置 flushCache=true,能够刷新二级缓存。默认状况下,select 语句中的 flushCache 是 false。如果是 insert、update、delete 语句,那么 flushCache 的默认值是 true。如果将 select 语句中的 flushCache 值改为 true,就意味着查问语句的二级缓存生效,每次查问都会从数据库进行查问。如果将 select 语句的 flushCache 值为 false,就代表该查问语句应用了二级缓存,如果在数据库中批改了数据,而二级缓存中的数据还是原来的数据,那么这样就会呈现脏读。
flushCache 设置如下: