- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- 简单易学
- 灵活
- sql与代码分离,易于维护
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供xml标签,支持编写动态sql
- 提供关系映射标签,支持对象关系组建维护
2、编写mybatis配置文件mybatis-config.xmlorg.mybatis mybatis 3.5.2 mysql mysql-connector-java 8.0.21 junit junit 4.11 test
db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&characterEncoding=utf-8&useUnicode=true&serverTimezone=GMT%2B8 username=root password=
2.2、SqlSession
其中包含了面向数据库执行sql语句的所有方法
package com.qhit.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 MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
//获取sqlSessionFactory对象
static {
String resource = "mybatis-config.xml";
try {
InputStream stream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(stream);
} catch (IOException e) {
e.printStackTrace();
}
}
//获取sqlSession对象
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.3、编写代码
1、实体类
package com.qhit.pojo;
public class User {
private int id;
private String usr;
private String pwd;
public User() {
}
public User(int id, String usr, String pwd) {
this.id = id;
this.usr = usr;
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", usr='" + usr + ''' +
", pwd='" + pwd + ''' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsr() {
return usr;
}
public void setUsr(String usr) {
this.usr = usr;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
2、Dao接口
package com.qhit.dao;
import com.qhit.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserMapper {
List getUsers(); //查询所有
User getUserById(int id); //ID查询
Integer addUser(User user); //单条插入
//map传参,参数自定
Integer addUserByMap(Map map);
Integer updateUser(User user); //单条更新
Integer deleteUserById(int id); //条件删除
}
3、XML
接口实现类由UserDaoImpl转化为一个Mapper配置文件
4、Mybatis配置文件中配置Mapper
5、测试类
package com.qhit.dao;
import com.qhit.pojo.User;
import com.qhit.util.MybatisUtils;
import junit.framework.TestCase;
import org.apache.ibatis.session.SqlSession;
import java.util.HashMap;
import java.util.Map;
public class UserTest extends TestCase {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
public void testGetUsers() {
for (User user : mapper.getUsers()) {
System.out.println(user);
}
}
public void testGetUserById() {
System.out.println(mapper.getUserById(1));
}
public void testAddUser() {
if (0 < mapper.addUser(new User(3, "Test1", "Test1"))) {
System.out.println("添加成功!");
sqlSession.commit();
}
}
public void testAddUserByMap() {
Map map = new HashMap<>();
map.put("id", 4);
map.put("username", "Test4");
map.put("password", "Test4");
if (mapper.addUserByMap(map) > 0){
System.out.println("更新成功!");
sqlSession.commit();
}
}
public void testUpdateUser() {
if(0 < mapper.updateUser(new User(3, "Test3", "Test3"))){
System.out.println("添加成功!");
sqlSession.commit();
}
}
public void testDeleteUserById() {
if(0 < mapper.deleteUserById(3)){
System.out.println("删除成功!");
sqlSession.commit();
}
}
}
6、可能遇见的问题
BindingException
问题描述
org.apache.ibatis.binding.BindingException: Type interface com.qhit.dao.UserDao is not known to the MapperRegistry.
解决方案
在mybatis配置文件中配置DaoExceptionInInitializerError
问题描述
java.lang.ExceptionInInitializerError
解决方案
资源过滤问题,在pom文件中加入以下代码
Cannot find class: com.mysql.jdbc.driversrc/main/resources ***.xml false src/main/java ***.xml false
问题描述
java.sql.SQLException: Error setting driver on UnpooledDataSource. Cause: java.lang.ClassNotFoundException: Cannot find class: com.mysql.jdbc.driver
解决方案
引入mysql依赖,对应数据库版本
查询数据库版本:SELECT VERSION();
Mapper中文报错mysql mysql-connector-java 8.0.21
问题描述
在mapper.xml中插入中文注释报错
解决方案
将mapper.xml文件上方UTF-8改为UTF8
- mybatis-config.xml
- 这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)3.2、环境配置(Environments)
MyBatis可以配置多种环境
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
包下类首字母可以小写,也可以使用注解
@Alias("author")
public class Author {
...
}
3.4、其他设置
- plugins插件
- mybatis-nenerator-core
- mybatis-plus
- 通用mapper
SqlSessionFactoryBuilder
- 一旦创建SqlSessionFactory就不被需要
- 方法作用域
SqlSessionFactory - 可以理解为数据库连接池
- 一旦创建就一直存在,没有任何理由丢弃或重新创建
- 应用作用域
- 最简单的就是使用单例或者静态单例模式
SqlSession - 到连接池的一个请求
- SqlSession不是线程安全的,因此不能被共享,最佳作用域是请求或方法作用域
- 用完之后立即关闭,防止资源被占用
如果一个数据库操作出现了异常,我们需要排错,日志就是最好的助手!
曾经:sout、debug
现在:日志工厂
- LSF4J
- LOG4J【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LONGGING
- STDOUT_LONGGING【掌握】
- NO_LONGGING
在Mybatis配置文件中配置我们的日志功能
4.2、LOG4J
**什么事LOG4J?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
- 导包
log4j log4j 1.2.17 - 创建log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码 log4j.rootLogger=DEBUG,console,file #控制台输出的相关设置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件输出的相关设置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日志输出级别 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
5、分页 5.1、分页插件PageHelper
1. 引入Maven依赖
com.github.pagehelper pagehelper 5.1.2
2. 配置SqlSessionFactory工厂
6、Association和Collection 6.1、多对一(Association)oracle true
多个学生对应一个教师
教师类
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
学生类
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
学生接口
public interface StudentMapper {
List getStudents();
Teacher getTeacherById(int id);
}
数据库
CREATE TABLE `student`( `id` INT PRIMARY KEY, `name` VARCHAR(255) NOT NULL `tid` int ); CREATE TABLE `teacher`( `id` INT PRIMARY KEY, `name` VARCHAR(255) NOT NULL );
Mapper.xml配置
方式一:联合查询方式二:按结果嵌套查询select * from student select * from teacher where id = #{id}
6.2、一对多(Collection)select s.*, t.id tid, t.name tname from student s, teacher t where s.tid = t.id
假如一个教师对应对个学生
教师类
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
private List students;
}
学生类
@Data
public class Student {
private int id;
private String name;
private int tid;
}
老师接口
public interface TeacherMapper {
Teacher getTeacherById(int id);
List getStudentsByTid(int tid);
}
数据库
CREATE TABLE `student`( `id` INT PRIMARY KEY, `name` VARCHAR(255) NOT NULL `tid` int ); CREATE TABLE `teacher`( `id` INT PRIMARY KEY, `name` VARCHAR(255) NOT NULL );
Mapper.xml配置
方式一:联合查询方式二:联合查询select s.*, t.id tid, t.name tname from student s, teacher t where s.tid = t.id and t.id = #{id}
8、动态SQL 8.1、ifselect t.id tid, t.name tname from teacher t where id = #{id} select * from student where tid = #{tid}
8.2、choose、when、otherwisseSELECT * FROM BLOG WHERe state = ‘ACTIVE’ AND title like #{title}
8.3、trim、where、setSELECT * FROM BLOG WHERe state = ‘ACTIVE’ AND title like #{title} AND author_name like #{author.name} AND featured = 1
若将 “state = ‘ACTIVE’” 设置成动态条件
SELECT * FROM BLOG WHERe state = #{state} AND title like #{title} AND author_name like #{author.name}
如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:
SELECT * FROM BLOG WHERe
MyBatis 有一个简单且适合大多数场景的解决办法。而在其他场景中,可以对其进行自定义以符合需求。而这,只需要一处简单的改动:
SELECT * FROM BLOG state = #{state} AND title like #{title} AND author_name like #{author.name}
where 元素只会在子元素返回任何内容的情况下才插入 “WHERe” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
...
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入prefix 属性中指定的内容。
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
update Author where id=#{id} username=#{username}, password=#{password}, email=#{email}, bio=#{bio}
这个例子中,set 元素会动态地在行首插入 SET关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
来看看与 set 元素等价的自定义 trim 元素吧:
8.4、foreach...
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
SELECT * FROM POST P WHERe ID in #{item}
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
8.5、bindbind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:
8.6、includeSELECT * FROM BLOG WHERe title LIKE #{pattern}
MyBatis中sql标签定义SQL片段,
include标签引用,可以复用SQL片段
sql标签中id属性对应include标签中的refid属性。通过include标签将sql片段和原sql片段进行拼接成一个完整的sql语句进行执行。
9、扩展 9.1、UUIDres_type_id,res_type select from pub_res_type
public static String getID(){
return UUID.randomUUID().toString().replaceAll("-", "");
}
public static void main(String[] args) {
System.out.println(getID());
System.out.println(getID());
System.out.println(getID());
}
输出
20c5e106ba9d4a9a8d266d25ac1f2489 a893931fa1334bafa031831ba2bb2ab0 db43c936d50f47c5823e4df400cbd07010、缓存 10.1、简介
查询: 连接数据库,耗资源! 一次查询的结果,给他暂存在一个可以直接取到的地方!-->内存:缓存 我们再次查询相同数据的时候,直接走缓存,就不用走数据库了
- 什么是缓存[Cache]?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
- MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
- Mybatis默认两级缓存:一级缓存与二级缓存
- 默认情况下只开启一级缓存(SqlSession级别的缓存,也称本地缓存);
- 二级缓存需手动配置开启(namespace级别的缓存);
- 为了提高扩展性,Mybatis提供了Cache缓存接口,我们可以用他来定义二级缓存;
- 一级缓存也叫本地缓存:
- 与数据库同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
- 映射语句文件中的所有select语句的结果将会被缓存。
- 映射语句文件中的所有insert, update和delete语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)缓存会保存列表或对象(无论查询方法返回哪种)的1024个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- Mybatis默认开启一级缓存,若要使用二级,只需在mapper.xml中加入cache标签
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;



