1.MyBatis的简介
概述:和数据库进行交互,持久化层框架(SQL映射框架)
操作数据库的其他工具和框架:
1)从原始的JDBC--》dbUtils(QueryRunner)--》jdbcTemplate,以上这些都称为工具,工具是对一些功能的简单封装,而框架则是某个领域的整体解决方案:考虑缓存,考虑异常处理问题,考虑部分字段映射问题等等
2)Hibernate:数据库交互的框架(ORM框架,全自动框架 )
Mybatis
-
MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
-
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
-
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录.
为什么要使用MyBatis?
1.MyBatis是一个半自动化的持久化层框架。
2.JDBC
-
SQL夹在Java代码块里,耦合度高导致硬编码内伤
-
维护不易且实际开发需求中sql是有变化,频繁修改的情况多见
3.Hibernate和JPA
-
长、难、复杂SQL,对于Hibernate而言处理也不容易
-
内部自动生产的SQL,不容易做特殊优化。
-
基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。导致数据库性能下降。
4.对开发人员而言,核心sql还是需要自己优化,
MyBatis框架中sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。
2.创建第一个MyBatis程序
创建第一个MyBatis的步骤:
1)导包:
- mybatis-3.4.1.jar
- mysql-connector-java-8.0.17.jar
- log4j-1.2.17.jar(建议导入,这样在mybatis关键的环节就会有日志打印,依赖类路径下一个log4f.xml配置文件)
2)写配置(全局配置文件和dao接口的实现文件)
- 第一个配置文件:成为mybatis的全局配置文件,知道mybatis如何正确运行,比如连接向哪个数据库
- 第二个配置文件:编写的每一个方法都是如何向数据库发送sql语句,如何执行的,相当于接口的实现类
- 我们写的dao接口的实现文件,MyBatis是不知道的,需要在全局配置文件中注册
3)测试
- 根据全局配置文件先创建一个sqlSessionFactory
- 从sqlSessionFactory中获取sqlSession对象操作数据库即可
代码实现:
环境部署:
数据库操作:
新建数据库(my):create database my;
新建User表:
create table user_01(
id int not null auto_increment PRIMARY KEY,
username VARCHAR(20),
`PASSWORD` VARCHAR(20),
role VARCHAR(20)
)
bean组件(User.java):
package com.luyi.bean;
public class User {
private Integer id;
private String username;
private String password;
private String role;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", role=" + role + "]";
}
}
dao层接口(UserDao.java)
package com.luyi.dao;
import com.luyi.bean.User;
public interface UserDao {
public User getUser(int i);
}
写配置:
第一个配置文件(mybatis-config.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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/my?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123"/>
</dataSource>
</environment>
</environments>
<!-- 引入我们自己编写的每一个接口实现文件 -->
<mappers>
<!-- resource:表示从类路径下找资源 -->
<mapper resource="UserDao.xml"/>
</mappers>
</configuration>
第二个配置文件(UserDao.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:名称空间,写接口的全类名,相当于告诉MyBatis这个配置文件是实现哪个接口的 -->
<mapper namespace="com.luyi.dao.UserDao">
<!-- select标签用来定义一个查询操作 -->
<!--
id:用来定义方法名,相当于这个配置是对某个方法的实现
resultType:指定方法运行后的返回值类型(查询操作必须指定)
-->
<select id="getUser" resultType="com.luyi.bean.User">
select * from user where id = #{id}
</select>
</mapper>
测试(MyBatisTest01.java文件):
package com.luyi.test;
import java.io.IOException;
import java.io.InputStream;
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 com.luyi.bean.User;
import com.luyi.dao.UserDao;
public class MyBatisTest01 {
public static void main(String[] args) throws IOException {
//根据全局配置文件创建一个SqlSessionFactory
//sqlSessionFactory:是SqlSession工厂,负责创建SqlSession对象
//sqlSession:sql会话(代表和数据库的一次会话)
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
User user = null;
SqlSession openSession = null;
try {
//获取和数据库的一次会话:相当于getConnection()
openSession = sqlSessionFactory.openSession();
//使用sqlSession操作数据库,获取到dao接口的实现
UserDao userDao = (UserDao) openSession.getMapper(UserDao.class);
user = userDao.getUser(3);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
System.out.println(user);
}
}
补充:Mybatis导入dtd文件使得写xml文件时有提示
MyBatis的CRUD
在上面程序进行简单的修改:
User.java:
package com.luyi.bean;
public class User {
private Integer id;
private String username;
private String password;
private String role;
public User(){};
public User(Integer id, String username, String password, String role) {
super();
this.id = id;
this.username = username;
this.password = password;
this.role = role;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", role=" + role + "]";
}
}
UserDao.java
package com.luyi.dao;
import com.luyi.bean.User;
public interface UserDao {
public User getUser(int i);
public int updateUser(User user);
public int deleteUser(int i);
public int addUser(User user);
}
测试文件:
package com.luyi.test;
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
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 com.luyi.bean.User;
import com.luyi.dao.UserDao;
public class Test01 extends TestCase {
public SqlSessionFactory initSqlSessionFactory() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public void testSelect() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
UserDao userDao = openSession.getMapper(UserDao.class);
try {
User user = userDao.getUser(3);
System.out.println(user);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
public void testUpdate() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
UserDao userDao = openSession.getMapper(UserDao.class);
User user = new User(3, "luyi", "123", "student");
try {
int row = userDao.updateUser(user);
System.out.println(row);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.commit();
openSession.close();
}
}
public void testDelete() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
UserDao userDao = openSession.getMapper(UserDao.class);
try {
int row = userDao.deleteUser(3);
System.out.println(row);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.commit();
openSession.close();
}
}
public void testInsert() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
UserDao userDao = openSession.getMapper(UserDao.class);
User user = new User(null, "天才", "77777", "student");
try {
int row = userDao.addUser(user);
System.out.println(row);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.commit();
openSession.close();
}
}
}
3.全局配置文件的配置
properties-引入外部配置文件
代码示例:
<?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配置文件 -->
<properties resource="dbconfig.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driverclass}"/>
<property name="url" value="${jdbcurl}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="UserDao.xml"/>
</mappers>
</configuration>
settings-修改mybatis的运行时行为
比较常用的是设置是否开启驼峰命名映射:
<!-- 改变 MyBatis 的运行时行为 -->
<settings>
<!-- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
typeAliases-给java类型起别名
代码示例(推荐还是使用全类名):
<typeAliases>
<!-- 默认起的别名是这个类名 -->
<typeAlias type="com.luyi.bean.User" alias="User"/>
<!-- 批量起别名,name指定包名 ,默认别名为这个类名
批量起别名,如果也想自定义别名,就在那个要起别名的bean上面加个@Alias("别名")注解-->
<package name="com.luyi.bean"/>
</typeAliases>
typeHandlers-类型处理器
概述:默认的类型处理器已经够我们的基本使用了,基本上不用再去注册自顶的类型处理器了
<typeHandlers>
<typeHandler handler="自定义的类型处理器"/>
</typeHandlers>
plugins插件
概述:插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为,插件通过动态代理机制,可以介入四大对象的任何一个方法的执行
- Executor:执行器,负责执行sql语句
- ParameterHandler:参数处理器,预编译搞好后,给预编译设置参数
- ResultSetHandler:结果处理器
- StatementHandler:用来预编译参数
environments-配置环境
代码示例:
<environments default="development">
<environment id="development">
<!-- JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。 -->
<transactionManager type="JDBC"/>
<!-- 有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”) -->
<dataSource type="POOLED">
<property name="driver" value="${driverclass}"/>
<property name="url" value="${jdbcurl}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
databaseIdProvider-用于数据库移植
代码示例:
全局配置文件:
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="SQL Server" value="sqlServer"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
SQL映射文件:
<select id="getUser" resultType="User" databaseId="mysql">
select * from user where id = #{id}
</select>
<select id="getUser" resultType="User" databaseId="oracle">
select * from user where id = #{id}
</select>
<select id="getUser" resultType="User" >
select * from user where id = #{id}
</select>
mappers-映射器
注册单个(代码示例):
<mappers>
<!--
url:可以从磁盘或者网络路径引用
resource:在类路径下找sql映射文件
class:直接引用接口的全类名;可以将xml放在和dao接口同目录下,而且文件名和接口名一致
class的另一种用法:把select,update,delete,insert标签里的sql语句通过注解直接写在dao文件对应的方法上,那么我们的class也只能是写这个有注解的类的全类名了
-->
<mapper class="com.luyi.dao.UserDao"/>
</mappers>
批量注册:
注意:批量注册这里的所有dao接口文件以及SQL映射配置文件都得放在com.luyi.dao包下才会注册成功,为了比较规范好看,我们可以在conf文件夹下重新新建一个也叫做com.luyi.dao的package
4.SQL映射文件
这个文件中的所有标签:
- cache:和缓存有关
- cache-ref:和缓存有关
- parameterMap:参数map,废弃的,原本是用来做复制参数映射
- insert,delete,upate,select
- sql:抽取1可重用的sql
insert,delete,upate,select里的属性
获取自增主键的值代码示例:
<!--
useGeneratedKeys="true":原生jdbc获取自增主键的方法
keyProperty="":将刚才自增的id封装给哪个属性
-->
<insert id="addUser" useGeneratedKeys="true" keyProperty="id">
insert into user(username, password, role) values(#{username}, #{password}, #{role})
</insert>
<!--再在测试文件中直接获取这个id的值是可以获取得到的-->
获取非自增主键的值的代码示例:
<insert id="addUser">
<!-- order="BEFORE":在核心sql语句之前先运行一个查询sql查到id,将查到的id赋值给javaBean的id属性 -->
<selectKey order="BEFORE" resultType="Integer" keyProperty="id">
select max(id) + 1 from user
</selectKey>
insert into user(username, password, role) values(#{username}, #{password}, #{role})
</insert>
SQL映射文件中参数传递
-
单个参数:
- 基本类型:取值的方法为:#{随便写都能取到这个参数}
-
多个参数
- 取值时通过#{参数名}是取不出来的
- 只能通过0,1(参数的索引)或者param1,param2(第几个参数)获取参数值
- 原因是只要传入了多个参数,mybatis会自动的将这些参数封装在一个map中,封装时使用的key就是参数的索引和参数的第几个表示
-
为参数指定key,命名参数,通过@Param注解实现,这是我们推荐的写法,我们可以告诉mybatis,封装参数map的时候别乱来,使用我们指定的key
-
传入map:直接使用#{key}取出
-
传入pojo:取出参数的方法为:#{pojo的属性名}
参数处理
参数也可以指定一个特殊的数据类型:
- javaType 通常可以从参数对象中来去确定
- 如果 null 被当作值来传递,对于所有可能为空的列,jdbcType 需要被设置,原因是null值对于oracle数据库是不认识的,会导致报错(常用设置)
- 对于数值类型,还可以设置小数点后保留的位数:
- mode 属性允许指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像在获取输出参数时所期望的那样。
- 参数位置支持的属性:
javaType、jdbcType、mode、numericScale、
resultMap、typeHandler、jdbcTypeName、expression
#{key}与${key}的区别
- #{key}:获取参数的值,预编译到SQL中。安全。
- ${key}:获取参数的值,拼接到SQL中。有SQL注入问题。ORDER BY ${name};${key}还可以用于不是参数的位置,如${user}(表名),而#{key}则不行
查询结果返回list
代码示例:
SQL映射文件:
...
<select id="getAll" resultType="com.luyi.bean.User">
select * from user
</select>
...
UserDao.java:
public List<User> getAll();
Test.java:
public void testSelect() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
UserDao userDao = openSession.getMapper(UserDao.class);
try {
List<User> list = userDao.getAll();
for(User user:list){
System.out.println(user);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
查询结果返回map
查询单条封装成map代码示例:
UserDao.xml:
<select id="getAllReturnMap" resultType="map">
select * from user where id = #{id}
</select>
UserDao.java:
public Map<String, Object> getAllReturnMap(int i);
Test.java:
public void testSelectReturnMap() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
UserDao userDao = openSession.getMapper(UserDao.class);
try {
Map<String, Object> map = userDao.getAllReturnMap(6);
System.out.println(map);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
查询多条返回Map代码示例:
UserDao.java:
//注解标志id为map的key
@MapKey("id")
public Map<Integer, User> getAllReturnMaps();
UserDao.xml:
<select id="getAllReturnMaps" resultType="com.luyi.bean.User">
select * from user
</select>
Test.java:
public void testSelectReturnMaps() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
UserDao userDao = openSession.getMapper(UserDao.class);
try {
Map<Integer, User> map = userDao.getAllReturnMaps();
System.out.println(map);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
自定义映射规则,自己定义每一列数据和javaBean的映射规则
默认的映射规则(mybatis自动封装结果集的规则):
- 按照列名和属性名一一对应的规则(不区分大小写)
- 如果不一一对应
- 开启驼峰命名法:满足驼峰命名规则的一一对应
- 起别名
自定义映射规则:
SQL映射文件:
...
<select id="getAll" resultMap="myUser">
select * from user
</select>
<!--type:指定为哪个javaBean自定义封装规则,id=“”:唯一标识,让别名在后面给引用-->
<resultMap type="com.luyi.bean.User" id="myUser">
<id property="id" column="id"/>
<result property="username" column="name"/>
<result property="password" column="pw"/>
<result property="role" column="role"/>
</resultMap>
联合查询(1对1)
环境搭建(数据库表的数据):
key表:
lock表:
代码实现(使用了if标签:OGNL表达式):
所有的bean文件:
//Key.java
package com.luyi.bean;
public class Key {
private Integer id;
private String keyName;
private Lock lock;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public Lock getLock() {
return lock;
}
public void setLock(Lock lock) {
this.lock = lock;
}
public String toString() {
return "Key [id=" + id + ", keyName=" + keyName + ", lock=" + lock
+ "]";
}
}
//Lock.java
package com.luyi.bean;
public class Lock {
private Integer id;
private String lockName;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLockName() {
return lockName;
}
public void setLockName(String lockName) {
this.lockName = lockName;
}
public String toString() {
return "Lock [id=" + id + ", lockName=" + lockName + "]";
}
}
dao文件:
//KeyDao.java
package com.luyi.dao;
import com.luyi.bean.Key;
public interface KeyDao {
public Key getKeyById(int id);
}
SQL映射配置文件:
<!--KeyDao.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">
<mapper namespace="com.luyi.dao.KeyDao">
<select id="getKeyById" resultMap="mykey">
select k.id kid, k.keyName, k.lockId, l.id lid, l.lockName
from `key` k
LEFT JOIN `LOCK` l ON
k.lockId = l.id where k.id = #{id}
</select>
<!-- 自定义封装规则:方式一:级联属性封装查出的数据 -->
<resultMap type="com.luyi.bean.Key" id="mykey">
<id property="id" column="kid"/>
<result property="keyName" column="keyName"/>
<result property="lock.id" column="lid"/>
<result property="lock.lockName" column="lockName"/>
</resultMap>
<!-- 自定义封装规则:方式二:association定义联合查询的对象 -->
<resultMap type="com.luyi.bean.Key" id="mykey">
<id property="id" column="kid"/>
<result property="keyName" column="keyName"/>
<!-- 接下来的属性是一个对象,自定义这个对象分封装规则,使用association:表示联合了一个对象 -->
<!-- javaType:指定这个属性的类型 -->
<association property="lock" javaType="com.luyi.bean.Lock">
<result property="id" column="lid"/>
<result property="lockName" column="lockName"/>
</association>
</resultMap>
</mapper>
properties文件(dbconfig):
username=root
password=123
jdbcurl=jdbc:mysql://localhost:3306/my?serverTimezone=UTC
driverclass=com.mysql.cj.jdbc.Driver
全局配置文件:
<!--mybatis-config.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>
<properties resource="dbconfig.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--properties属性引入properties配置文件 -->
<dataSource type="POOLED">
<property name="driver" value="${driverclass}"/>
<property name="url" value="${jdbcurl}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="KeyDao.xml"/>
</mappers>
</configuration>
测试文件(Test01.java):
package com.luyi.test;
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
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 com.luyi.bean.Key;
import com.luyi.dao.KeyDao;
public class Test01 extends TestCase {
public SqlSessionFactory initSqlSessionFactory() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public void test01() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
try {
KeyDao mapper = (KeyDao) openSession.getMapper(KeyDao.class);
Key key = mapper.getKeyById(1);
System.out.println(key);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
}
联合查询(1对多)
在联合查询(1对1)的代码实现基础上进行一下修改:
Lock.java:
package com.luyi.bean;
import java.util.List;
public class Lock {
private Integer id;
private String lockName;
private List<Key> keys;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLockName() {
return lockName;
}
public void setLockName(String lockName) {
this.lockName = lockName;
}
public List<Key> getKeys() {
return keys;
}
public void setKeys(List<Key> keys) {
this.keys = keys;
}
@Override
public String toString() {
return "Lock [id=" + id + ", lockName=" + lockName + ", keys=" + keys
+ "]";
}
}
LockDao.java:
package com.luyi.dao;
import com.luyi.bean.Lock;
public interface LockDao {
//查询锁时把所有的钥匙也查询出来
public Lock getLockById(int id);
}
LockDao.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">
<mapper namespace="com.luyi.dao.LockDao">
<select id="getLockById" resultMap="myLock">
select k.id kid, k.keyName, k.lockId, l.id lid, l.lockName
from `key` k
LEFT JOIN `LOCK` l ON
k.lockId = l.id where l.id = #{id}
</select>
<!-- 自定义封装规则,collection标签定义集合类型分封装 -->
<resultMap type="com.luyi.bean.Lock" id="myLock">
<id property="id" column="lid"/>
<result property="lockName" column="lockName"/>
<!-- collection:定义集合元素的封装
propertype="":指定哪个属性是集合属性
ofType="":指定集合里面元素的类型 -->
<collection property="keys" ofType="com.luyi.bean.Key">
<!-- 标签体中指定集合中这个元素的封装规则 -->
<id property="id" column="kid"/>
<result property="keyName" column="keyName"/>
</collection>
</resultMap>
</mapper>
测试文件:Test01.java:
package com.luyi.test;
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
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 com.luyi.bean.Key;
import com.luyi.bean.Lock;
import com.luyi.dao.KeyDao;
import com.luyi.dao.LockDao;
public class Test01 extends TestCase {
public SqlSessionFactory initSqlSessionFactory() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public void test02() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
try {
LockDao mapper = openSession.getMapper(LockDao.class);
Lock lock = mapper.getLockById(3);
System.out.println(lock);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
}
联合查询(association分步查询)
代码实现(在联合查询(1对多)的代码实现上进行修改):
KeyDao.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">
<mapper namespace="com.luyi.dao.KeyDao">
<select id="getKeyById" resultMap="mykey">
select * from `key` where id = #{id}
</select>
<!-- 自定义封装规则,association定义联合查询的对象 -->
<resultMap type="com.luyi.bean.Key" id="mykey">
<id property="id" column="kid"/>
<result property="keyName" column="keyName"/>
<!-- 告诉mybatis自己去调用一个查询查询锁
select="":指定一个查询sql的唯一标识,mybatis自动调用指定的sql将查出的lock封装进来
column:指定将哪一列的数据传递过去
-->
<association property="lock"
select="com.luyi.dao.LockDao.getLockById"
column="id"
>
</association>
</resultMap>
</mapper>
LockDao.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">
<mapper namespace="com.luyi.dao.LockDao">
<select id="getLockById" resultType="com.luyi.bean.Lock" >
select * from `lock` where id = #{id}
</select>
</mapper>
注意:测试方法使用的是test01方法测试;
联合查询(按需加载和延迟加载)
按需加载的配置:
<settings>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启属性按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
注意:设置了按需加载的配置之后,使用分步查询时,不会马上两条查询语句都发出去,而是按需加载,当我们需要使用另一个bean的属性时,才发送查询语句过去查询获取这个属性
联合查询(collection分步查询延迟加载):
代码实现:
所有的bean文件:
//Key.java
package com.luyi.bean;
public class Key {
private Integer id;
private String keyName;
private Lock lock;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getKeyName() {
return keyName;
}
public void setKeyName(String keyName) {
this.keyName = keyName;
}
public Lock getLock() {
return lock;
}
public void setLock(Lock lock) {
this.lock = lock;
}
public String toString() {
return "Key [id=" + id + ", keyName=" + keyName + ", lock=" + lock
+ "]";
}
}
//Lock.java
package com.luyi.bean;
import java.util.List;
public class Lock {
private Integer id;
private String lockName;
private List<Key> keys;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLockName() {
return lockName;
}
public void setLockName(String lockName) {
this.lockName = lockName;
}
public List<Key> getKeys() {
return keys;
}
public void setKeys(List<Key> keys) {
this.keys = keys;
}
@Override
public String toString() {
return "Lock [id=" + id + ", lockName=" + lockName + ", keys=" + keys
+ "]";
}
}
所有的dao文件:
//KeyDao.java
package com.luyi.dao;
import java.util.List;
import com.luyi.bean.Key;
public interface KeyDao {
public Key getKeyById(int id);
public List<Key> getKeyByLockId(int id);
}
//LockDao.java
package com.luyi.dao;
import com.luyi.bean.Lock;
public interface LockDao {
//查询锁时把所有的钥匙也查询出来
public Lock getLockById(int id);
}
配置文件:
dbconfig.properties:
username=root
password=123
jdbcurl=jdbc:mysql://localhost:3306/my?serverTimezone=UTC
driverclass=com.mysql.cj.jdbc.Driver
mybatis-config.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>
<properties resource="dbconfig.properties"></properties>
<settings>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启属性按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--properties属性引入properties配置文件 -->
<dataSource type="POOLED">
<property name="driver" value="${driverclass}"/>
<property name="url" value="${jdbcurl}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="KeyDao.xml"/>
<mapper resource="LockDao.xml"/>
</mappers>
</configuration>
log4j.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L)
" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
KeyDao.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">
<mapper namespace="com.luyi.dao.KeyDao">
<select id="getKeyByLockId" resultType="com.luyi.bean.Key">
select * from `key` where lockId = #{id}
</select>
<select id="getKeyById" resultMap="mykey">
select * from `key` where id = #{id}
</select>
<!-- 自定义封装规则,association定义联合查询的对象 -->
<resultMap type="com.luyi.bean.Key" id="mykey">
<id property="id" column="kid"/>
<result property="keyName" column="keyName"/>
<!-- 告诉mybatis自己去调用一个查询查询锁
select="":指定一个查询sql的唯一标识,mybatis自动调用指定的sql将查出的lock封装进来
column:指定将哪一列的数据传递过去
-->
<association property="lock"
select="com.luyi.dao.LockDao.getLockById"
column="id"
>
</association>
</resultMap>
</mapper>
LockDao.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">
<mapper namespace="com.luyi.dao.LockDao">
<select id="getLockById" resultMap="myLock" >
select * from `lock` where id = #{id}
</select>
<!-- collection分布查询 -->
<resultMap type="com.luyi.bean.Lock" id="myLock">
<id property="id" column="id"/>
<result property="lockName" column="lockName"/>
<collection property="keys"
select="com.luyi.dao.KeyDao.getKeyByLockId"
column="id">
</collection>
</resultMap>
</mapper>
测试文件(Test01.java):
package com.luyi.test;
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
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 com.luyi.bean.Key;
import com.luyi.bean.Lock;
import com.luyi.dao.KeyDao;
import com.luyi.dao.LockDao;
public class Test01 extends TestCase {
public SqlSessionFactory initSqlSessionFactory() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public void test01() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
try {
KeyDao mapper = (KeyDao) openSession.getMapper(KeyDao.class);
Key key = mapper.getKeyById(1);
System.out.println(key.getLock().getLockName());
Thread.sleep(3000);
System.out.println(key.getKeyName());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
public void test02() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
try {
LockDao mapper = openSession.getMapper(LockDao.class);
Lock lock = mapper.getLockById(3);
System.out.println(lock.getLockName());
System.out.println(lock.getKeys());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
}
6.动态sql
概述:方便了我们拼接数据库表字符
第一个动态sql程序:
环境部署:
代码实现:
bean文件:
//Teacher.java
package com.luyi.bean;
import java.util.Date;
public class Teacher {
private Integer id;
private String teacherName;
private String address;
private Date birth;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTeacherName() {
return teacherName;
}
public void setTeacherName(String teacherName) {
this.teacherName = teacherName;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Teacher [id=" + id + ", teacherName=" + teacherName
+ ", address=" + address + ", birth=" + birth + "]";
}
}
dao文件:
//TeacherDao.java
package com.luyi.dao;
import com.luyi.bean.Teacher;
public interface TeacherDao {
public Teacher getTeacherByCondition(Teacher teacher);
}
SQL映射文件(TeacherDao.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">
<mapper namespace="com.luyi.dao.TeacherDao">
<select id="getTeacherByCondition" resultType="com.luyi.bean.Teacher">
select * from Teacher where
<!-- if标签判断条件进行sql拼接 -->
<if test="id!=null">
id > #{id}
</if>
<if test="teacherName != null and teacherName != ''">
and teacherName like #{teacherName}
</if>
<if test="birth != null">
and birth < #{birth}
</if>
</select>
</mapper>
dbconfig.properties:
username=root
password=123
jdbcurl=jdbc:mysql://localhost:3306/my?serverTimezone=UTC
driverclass=com.mysql.cj.jdbc.Driver
mybtais-config.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>
<properties resource="dbconfig.properties"></properties>
<settings>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 开启属性按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<!--properties属性引入properties配置文件 -->
<dataSource type="POOLED">
<property name="driver" value="${driverclass}"/>
<property name="url" value="${jdbcurl}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="TeacherDao.xml"/>
</mappers>
</configuration>
测试文件(Test01.java):
package com.luyi.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import junit.framework.TestCase;
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 com.luyi.bean.Teacher;
import com.luyi.dao.TeacherDao;
public class Test01 extends TestCase {
public SqlSessionFactory initSqlSessionFactory() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
public void test01() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
try {
TeacherDao teacherDao = openSession.getMapper(TeacherDao.class);
Teacher teacher = new Teacher();
teacher.setId(1);
teacher.setTeacherName("赵%%");
teacher.setBirth(new Date());
System.out.println(teacher);
Teacher result = teacherDao.getTeacherByCondition(teacher);
System.out.println(result);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
}
补充:where标签可以替代where关键字,帮我们去除写在前面的and,防止报错,如:
<select id="getTeacherByCondition" resultType="com.luyi.bean.Teacher">
select * from Teacher
<where>
<!-- if标签判断条件进行sql拼接 -->
<if test="id!=null">
and id > #{id}
</if>
<if test="teacherName != null and teacherName != ''">
and teacherName like #{teacherName}
</if>
<if test="birth != null">
and birth < #{birth}
</if>
</where>
</select>
trim标签的使用:
trim截取字符串:
- prefix="":前缀,为我们下面的sql整体添加一个前缀
- prefixOverrides="" :取出整体字符串前面多余的字符
- suffix="":为整体添加一个后缀
- suffixOverrides="":后面哪个多了可以去掉
代码示例:
<select id="getTeacherByCondition" resultType="com.luyi.bean.Teacher">
select * from Teacher
<!-- trim截取字符串:
prefix="":前缀,为我们下面的sql整体添加一个前缀
prefixOverrides="" :取出整体字符串前面多余的字符
suffix="":为整体添加一个后缀
suffixOverrides="":后面哪个多了可以去掉
-->
<trim prefix="where" prefixOverrides="and" suffixOverrides="and">
<!-- if标签判断条件进行sql拼接 -->
<if test="id!=null">
and id > #{id}
</if>
<if test="teacherName != null and teacherName != ''">
and teacherName like #{teacherName}
</if>
<if test="birth != null">
and birth < #{birth}
</if>
</trim>
</select>
foreach标签的使用
foreach标签遍历集合:
- collection="":指定要遍历的集合的key
- close="":以什么作为结束符号
- index="i" :索引,
- 如果遍历的是一个list:index,指定的变量保存当前索引;
- 如果遍历的是一个map,index指定的变量就是保存了当前遍历的key
- item="变量名":每次遍历出的元素起一个变量名方便引用
- open="":以什么作为开始符号
- separator="":每次遍历的元素的分隔符
代码示例(在动态sql的第一个程序案例的基础上进行修改):
TeacherDao.java:
package com.luyi.dao;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import com.luyi.bean.Teacher;
public interface TeacherDao {
public Teacher getTeacherByCondition(Teacher teacher);
public List<Teacher> getTeachersByids(@Param("ids")List<Integer> ids);
}
TeacherDao.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">
<mapper namespace="com.luyi.dao.TeacherDao">
<select id="getTeacherByCondition" resultType="com.luyi.bean.Teacher">
select * from Teacher
<!-- trim截取字符串:
prefix="":前缀,为我们下面的sql整体添加一个前缀
prefixOverrides="" :取出整体字符串前面多余的字符
suffix="":为整体添加一个后缀
suffixOverrides="":后面哪个多了可以去掉
-->
<trim prefix="where" prefixOverrides="and" suffixOverrides="and">
<!-- if标签判断条件进行sql拼接 -->
<if test="id!=null">
and id > #{id}
</if>
<if test="teacherName != null and teacherName != ''">
and teacherName like #{teacherName}
</if>
<if test="birth != null">
and birth < #{birth}
</if>
</trim>
</select>
<select id="getTeachersByids" resultType="com.luyi.bean.Teacher">
select * from teacher where id in
<!--
collection="":指定要遍历的集合的key
close="":以什么作为结束符号
index="i" :索引,
如果遍历的是一个list:index,指定的变量保存当前索引;
如果遍历的是一个map,index指定的变量就是保存了当前遍历的key
item="变量名":每次遍历出的元素起一个变量名方便引用
open="":以什么作为开始符号
separator="":每次遍历的元素的分隔符
-->
<foreach collection="ids" close=")" index="" item="id_item" open="(" separator=",">
#{id_item}
</foreach>
</select>
</mapper>
Test01.java:
package com.luyi.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import junit.framework.TestCase;
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 com.luyi.bean.Teacher;
import com.luyi.dao.TeacherDao;
public class Test01 extends TestCase {
public SqlSessionFactory initSqlSessionFactory() throws IOException{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
/**
* 测试第一个动态sql程序
* @throws IOException
*/
public void test01() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
try {
TeacherDao teacherDao = openSession.getMapper(TeacherDao.class);
Teacher teacher = new Teacher();
teacher.setId(1);
//teacher.setTeacherName("赵%%");
teacher.setBirth(new Date());
System.out.println(teacher);
Teacher result = teacherDao.getTeacherByCondition(teacher);
System.out.println(result);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
/**
* 测试foreach标签的使用
* @throws IOException
*/
public void test02() throws IOException{
SqlSession openSession = initSqlSessionFactory().openSession();
try {
TeacherDao teacherDao = openSession.getMapper(TeacherDao.class);
List<Integer> list = Arrays.asList(1, 2, 3);
List<Teacher> result = teacherDao.getTeachersByids(list);
System.out.println(result);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
openSession.close();
}
}
}
choose标签的使用
概述:choose标签中当when条件满足时,就不会往下走了
代码实现:
<trim prefix="where" prefixOverrides="and" suffixOverrides="and">
<choose>
<when test="id != null">
id=#{id} and
</when>
<when test="teacherName != null and teacherName != ''">
teacherName like teacherName and
</when>
<when test="birth != null">
birth < #{birth}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</trim>
set标签结合if标签完成mybatis动态更新
概述:set替代了set关键字,并可以去除多余的逗号
代码实现:
<select id="updateTeacher" resultType="com.luyi.bean.Teacher">
UPDATE teacher
<!-- set替代了set关键字,并可以去除多余的逗号 -->
<set>
<if test="teacherName != null and teacherName != ''">
teacherName=#{teacherName},
</if>
<if test="address != null and address != ''">
address = #{address},
</if>
<if test="birth != null">
birth = #{birth},
</if>
</set>
where id = #{id}
</select>
其他标签
1.bind标签:把一个表达式绑定到一个变量上
2.sql标签:抽取出可重用的sql语句
OGNL的介绍
OGNL(Object Graph Navigation Language),也就是对象导航图语言,这是一种强大的
表达式语言,通过它可以非常方便的来操作对象属性,使用方法就是级联属性一级一级往下调用即可
其他两个参数:
概述:在mybatis中,传入的参数可以用来做判断,额外还有两个参数_parameter和_databaseId
-
_parameter:代表传进来的参数
- 传入了单个参数,_parameter就代表这个参数
- 传入了多个参数,_parameter就代表多个参数集合起来的map
-
_databaseId:代表当前环境,如果配置了databaseIdProvider,_databaseId就有值
7.缓存机制(用Map保存查询出来的一些数据)
概述:MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存(一级缓存和二级缓存):
- 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
一级缓存(默认存在)
概述:只要之前查询过的数据,mybatis就会保存在一个缓存中(Map);下次获取直接从缓存中拿,需要注意的是一级缓存是sqlSession级别的缓存,sqlSession关闭或者提交以后,一级缓存的数据就会放在二级缓存中
一级缓存失效的几种情况:
1.不同的sqlSession使用不同的一级缓存
- 只有在同一个sqlSession期间查询到的数据会保存在这个sqlSession的缓存中,下次使用这个sqlSession查询会从缓存中拿
2.同一个方法,不同的参数,由于可能之前没查询过,所以还会发新的sql
3.在这个sqlSession期间,如果有增删改操作,就会清空之前的缓存
4.手动清空缓存:通过openSession.clearCache()方法清空
二级缓存
概述:全局作用域缓存,二级缓存默认不启动,需要手动配置,MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口,二级缓存在sqlSession关闭或提交之后才生效;二级缓存是namespace级别的缓存,说它是namespace级别的缓存是因为使用二级缓存的映射文件处需要使用chache标签进行配置,而这个映射文件映射的就是namespace指定的类
步骤:
-
全局配置文件中开启二级缓存:<setting name="cacheEnabled" value="true"/>
-
需要使用二级缓存的映射文件处使用cache配置缓存:<cache><cache/>
-
POJO需要实现Serializable接口
缓存的相关属性(了解)
概述:缓存的相关属性,也就是cache标签里面的属性
- eviction=“FIFO”:缓存回收策略:
LRU – 最近最少使用的:移除最长时间不被使用的对象。 - FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
- WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
默认的是 LRU。 - flushInterval:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新 - size:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出 - readOnly:只读,true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
缓存的查询顺序
1.不会出现一级缓存和二级缓存中有同一个数据
- 二级缓存中,一级缓存关闭了就有了
- 一级缓存中,二级缓存中没有此数据,就会看一级缓存,一级缓存没有去查数据库,数据库的查询后的结果放在一级缓存中
2.任何时候都是先看二级缓存,再看一级缓存,都没有,再去数据库查询
缓存有关设置
- 全局setting的cacheEnable:
配置二级缓存的开关。一级缓存一直是打开的。 - select标签的useCache属性:
配置这个select是否使用二级缓存。一级缓存一直是使用的 - sql标签的flushCache属性:
增删改默认flushCache=true。sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false。 - sqlSession.clearCache():
只是用来清除一级缓存。 - 当在某一个作用域 (一级缓存Session/二级缓存Namespaces) 进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。
整合第三方缓存
概述:MyBatis自带的缓存的功能没有很强大,往往需要整合进其他一些优秀的缓存来进行MyBatis缓存的使用,我们可以通过Cache接口实现整合第三方缓存
下面我们通过整个EhCache来介绍整合第三方缓存的步骤:
EhCache概述:EhCache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvide
MyBatis定义了Cache接口方便我们进行自定义扩展。
步骤:
- 导入ehcache包,以及整合包,日志包
ehcache-core-2.6.8.jar、mybatis-ehcache-1.0.3.jar
slf4j-api-1.6.1.jar、slf4j-log4j12-1.6.2.jar - 编写ehcache.xml配置文件
- 配置cache标签
- 参照缓存:若想在命名空间中共享相同的缓存配置和实例。可以使用 cache-ref 元素来引用另外一个缓存。