声明:全文都是来自狂神说Java,纯属个人学习用,视频请移步下面的网址:
1、简介 1.1、什么是Mybatishttps://www.bilibili.com/video/BV1WE411d7Dv?t=33.3
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。
- 2013年11月迁移到Github。
如何获得Mybatis?
- maven仓库
1.2、持久化org.mybatis mybatis 3.5.6
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存:断电即失
- 数据库(jdbc),io文件持久化
为什么需要持久化?
- 有些对象,不能让他丢掉
- 内存太贵了
- 完成持久化工作的代码块
- 层界限十分明显
- 帮助程序员将数据存入数据库中
- 方便
- 传统的JDBC代码太复杂了。简化,框架,自动化
思路:搭建环境–>导入Mybatis–>编写代码–>测试
2.1、搭建环境搭建数据库
CREATE DATAbase `mybatis`; USE `mybatis`; create TABLE `user`( `id` INT(20) Not null PRIMARY KEY, `name` varchar(20) DEFAULT NUll, `pwd` VARCHAr(20) default NULL )ENGINE=INNODB default CHARSET=utf8; insert into `user`(`id`,`name`,`pwd`) VALUES (1,'张三','123456'), (2,'李四','123789'), (3,'王五','258963')
新建项目
-
新建一个普通的maven项目
-
删除src目录,注意:新建好项目后,检查箭头指的地方是不是自己maven所在路径箭头
-
导入maven依赖
2.3、编写代码mysql mysql-connector-java5.1.48 org.mybatis mybatis3.5.6 junit junit3.8.1 test
- 实体类
public class User {
private int id;
private String name;
private String pwd;
//无参
public User() {
super();
}
//有参
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
- Dao接口
public interface UserDao {
List getUserList();
}
- 接口实现类由原来的UserDaoImpl接口转换为Mapper配置文件
2.4、测试select * from mybatis.user
注意点:
org.apache.ibatis.binding.BindingException: Type interface com.hou.dao.UserDao is not known to the MapperRegistry.
namespace中的包名要和Dao/Mapper接口的包名一致。
2、select选择查询语句:
- id:就是对应的namespace中的方法名;
- resultType:SQL语句执行的返回值;
- parameterType:参数类型。
- 编写接口
//按ID获取对象 User getUserById(int id);
- 编写对应的mapper中的SQL语句
- 测试
@Test public void getUserById(){ //第一步,获取SQLSession对象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //第二步:执行sql UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }
4、updateinsert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd});
5、deleteupdate mybatis.user set name=#{name},pwd=#{pwd} where id = #{id};
注意点:增删改要提交事务(以update为例说明)delete from mybatis.user where id = #{id}
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.updateUser(new User(4, "嘿嘿", "121121"));
if(res>0){
System.out.println("修改成功!");
}
//提交事务
sqlSession.commit();
//关闭事务
sqlSession.close();
}
6、分析问题
1
在CRUD这块编写过程中遇到的问题:
在UserMapper.xml文件中编写的sql语句后,每次在运行test测试文件时,总是会出现org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hou.dao.UserMapper.updateUser错误,
目前网上的说法是因为maven的约定大于配置,所以在一开始我们需要在pom.xml中加入以下代码,确保我们的资源不被过滤掉:
src/main/resources ***.xml true src/main/java ***.xml true
但是在我具体的操作过程中,这样做也没用,所以我采用的是手动的把UserMapper.xml复制到target下,而且每次在更新UserMapper.xml文件时,相应的把更新的数据加入到target下,这样我们的测试程序就能正常运行起来。
2
**错误:**使用mybatis测试时,前几次都可以正常的跑,后几次莫名其妙的报错:
Cause: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: 2 字节的 UTF-8 序列的字节 2 无效
检查:把所有的程序又敲了一遍,保证无误,但运行的时候还是出现了同样的错误。
解决方案:
在pom.xml文件中配置:
UTF-8
关于测试程序的一些tips:通常我们采用Juint单元测试,在test文件夹下,我们需要创建与上面一样的包结构,确保测试的一致性。
7、万能的Map假设,我们的实体类,或者数据库中的表,字段或者参数过多,我们应当使用Map。
//使用Map去增加用户
int addUser2(Map map);
insert into mybatis.user (id,pwd) value(#{userId},#{password});
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap();
map.put("userId",6);
map.put("password","123852");
mapper.addUser2(map);
sqlSession.close();
}
Map传递参数,直接在sql中取出对应的key即可,适用于多个是参数的情况。(多个参数的话还可以使用注解!)
对象传递参数,直接在sql中取对象的属性即可,适用于参数少的情况。
1.Java代码执行时,要使用通配符%%
Listuser = mapper.getUserList1("%王%");
2.在sql中拼接使用通配符(通配符要加" ")
select * from mybatis.user where name like "%"#{value}"%"
4、配置解析
1、核心配置文件
- mybatis-config.xml
- Mybatis的配置文件包含了会深深影响Mybatis行为的设置和属性信息
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
2、环境配置(environments)
MyBatis可以配置适应多种环境
尽管可以配置多个环境,但每个SQLSessionFactory实例只能选择一种环境。
通过default去选择环境id
Mybatis默认的事务管理器是JDBC,连接池POOLED
3、属性(properties)我们可以通过properties属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
编写一个配置文件
db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root password=root
在核心配置中引入
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件的。
- 类型别名是为Java类型设置一个短的名字。
- 存在的意义仅在于用来减少类完全限定名的冗余
没有用类型别名时
使用类型别名后
5、设置(setting)select * from mybatis.user ``` 也可以指定一个包名,MyBatis会在包名下面搜索需要的Java Bean ,比如:扫描实体类的包,它的默认别名就为这个类的类名,首字母小写! ```xml``` 在实体类比较少的时候,使用第一种方式。 在实体类比较多的时候,使用第二种。 第一种可以自定义别名,第二种不行。如果需要的话,可以在实体类上加注解 ```java @Alias("author") public class Author { ... }
这些是非常重要的调整,可以修改 MyBatis 在运行时行为。
一般我们使用相对于类路径的资源引用
7、生命周期和作用域
生命周期和作用域是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder:
- 一旦创建了SQLSessionFactory,就不再需要它了
- 局部变量
SQLSessionFactory: - 相当于数据库连接池
- SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃或重新创建另一个实例。
SqlSession - 连接到连接池的一个请求
- SQLSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后需要赶紧关闭,否则资源被占用
这里的每一个Mapper,就代表一个具体的业务!
1、问题
当数据库中的字段与实体类中的字段不一致时。
例:数据库中user表中的一个字段为pwd,但在实体类中表示为password,这样造成字段不一致,导致sql语句在执行时会使得password=null。
2、解决办法
- 起别名:
select id,name,pwd as password from mybatis.user where id = #{id}
- ResultMap结果集映射
id name pwd id name password
6、日志 6.1、日志工厂select * from mybatis.user where id = #{id}
如果一个数据库操作出现了异常,我们需要排错,日志就是最好的帮手。
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
在mybatis中具体使用哪一个日志实现,在设置中设定。
STDOUT_LOGGING标准日志输出
在mybatis核心配置文件中,配置我们的日志。
加入日志后的运行结果图
什么是log4j?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件。
- 我们也可以控制每一条日志的输出格式。
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1.先导入log4j的包
log4j log4j 1.2.17
2.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.FileAppender
log4j.appender.file.File = ./log/hou.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=DEBUGE
log4j.logger.java.sql.Statement=DEBUGE
log4j.logger.java.sql.ResultSet=DEBUGE
log4j.logger.java.sql.PreparedStatement=DEBUGE
3、配置log4j为日志的实现
4、log4j的使用
简单使用
- 在要使用Log4j的类中,导入包import org.apache.log4j.Logger;(容易导错)
- 日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(String.valueOf(UserMapperTest.class));
- 日志级别
logger.info("info:进入testLog4j");
logger.debug("info:进入testLog4j");
logger.error("info:进入testLog4j");
7、分页
为什么要减少分页?
- 减少数据的处理量
使用limit分页
select * from user limit startIndex,pageSize;
使用Mybatis实现分页,核心SQL
- 接口
ListgetUserList(Map map);
- Mapper.xml
select * from mybatis.user limit #{startIndex},#{pageSize}
- 测试
public void getUserList(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap<>();
map.put("startIndex",1);
map.put("pageSize",2);
List users = mapper.getUserList(map);
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
8、使用注解开发
8.1、面向接口编程
我们都学过面向对象编程,但在真正的开发中,我们很多时候都会选择面向接口编程。
根本原因:解耦,可拓展,提高复用,分层开发中,上层不用管具体的实现,大家都遵守共同的标准,使得开发变得更容易,规范性更好
-
注解在接口上实现
public interface UserMapper { @Select("select * from user") ListgetUsers(); } -
绑定接口
-
测试
我们可以在工具类创建的时候实现自动提交事务!
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
编写接口,增加注解
public interface UserMapper {
@Select("select * from user")
List getUsers();
//多个参数时,所有参数前面必须加上@Param("id")注解
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id") int id);
}
测试类
【注意:我们必须要将接口注册绑定到我们的核心配置文件中!】
关于@Param()注解
- 基本类型的参数或者String类型,需要加上
- 引用类型不需要加
- 如果只有一个基本类型的话,可以忽略,但建议加上
- 我们在SQL中引用的就是我们这里@Param()中设定的属性名
例如老师和学生
- 对于学生这边而言,关联 多个学生关联一个老师【多对一】
- 对于老师而言,集合 , 一个老师,有很多学生【一对多】
create table `student` (
`id` int(10) not null,
`name` varchar(30) DEFAULT null,
PRIMARY key (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
create table `student` (
`id` int(10) not null,
`name` varchar(30) DEFAULT null,
`tid` int(10) DEFAULT null,
PRIMARY key (`id`),
key `fktid` (`tid`),
ConSTRAINT `fktid` FOREIGN key (`tid`) REFERENCES `teacher` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
insert into `student` (`id`,`name`,`tid`) VALUES('1','王红','1');
insert into `student` (`id`,`name`,`tid`) VALUES('2','黎明','1');
insert into `student` (`id`,`name`,`tid`) VALUES('3','君山','1');
insert into `student` (`id`,`name`,`tid`) VALUES('4','宝达','1');
测试环境搭建
- 建立实体类Teacher,Student
- 建立对应的Mapper接口
- 建立对应的Mapper.xml文件
- 在核心配置文件中绑定注册我们的Mapper接口或者文件
- 测试查询是否能成功。
按照查询嵌套处理
按照结果嵌套处理
select s.id sid,s.name sname,t.name tname
from student s,teacher t
where s.tid = t.id
11、一对多处理
1、环境搭建
和刚才一样
实体类
public class Student {
private int id;
private String name;
private int tid;
}
public class Teacher {
private int id;
private String name;
//一个老师包含多个学生
private List students;
}
按照结果嵌套查询
select s.id sid,s.name sname,t.name tname,t.id,tid
from student s,teacher t
where s.tid = t.id and t.id = #{tid}
按照查询嵌套查询
select * from teacher where id = #{tid} select * from student where tid = #{tid}
小结
- 关联 - association 【多对一】
- 集合 - collection 【一对多】
- JavaType 用来指定实体类中属性的类型
- ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!
注意点:
- 保证SQL的可读性,尽量保证通俗易懂
- 注意一对一和多对一中属性名和字段的问题
- 如果问题不好拍错,使用日志Log4j
什么是动态SQL
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
搭建环境
CREATE table `blog`( `id` varchar(50) not null COMMENT '博客id', `title` varchar(100) not null COMMENT '博客标题', `author` varchar(30) not null COMMENT '作者', `create_time` datetime not null COMMENT '创建时间', `views` int(30) not null COMMENT '浏览量' )ENGINE=INNODB DEFAULT CHARSET=utf8
搭建一个基础工程
- 导包
- 编写配置文件
- 编写实体类
public class Blog {
private int id;
private String title;
private String author;
private Date createTime;
private int views;
public Blog() {
super();
}
public Blog(int id, String title, String author, Date createTime, int views) {
this.id = id;
this.title = title;
this.author = author;
this.createTime = createTime;
this.views = views;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public int getViews() {
return views;
}
public void setViews(int views) {
this.views = views;
}
}
- 编写实体类对应Mapper接口和Mapper.xml文件
IF
select * from blog where 1=1 and title = #{title} and author = #{author}
ForEach
foreach元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量,它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符,这个元素是很智能的,因此他不会偶然地附加多余的分隔符。
select * from blog
id = #{id}
动态SQL就是在拼接SQL语句,我们只要保证SQL语言的正确性,按照SQL的格式,去排列组合就可以了。
13、缓存 13.1、简介- 什么是缓存?
- 存在内存中的临时数据
- 将用户经常查询的数据存在放在缓存中
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高效率
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
- Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地制定和配置缓存。缓存可以极大的提升查询效率。
- Mybatis系统中默认定义了两级缓存:一级缓存和二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,它是基于namespace级别的缓存。
- 为了提高扩展性,Mybatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
- 一级缓存也叫本地缓存:SqlSession
- 与数据同一次会话期间查询到的数据会放在本地缓存中。
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;
测试步骤:
1. 开启日志 2. 测试在一个Session中查询两次相同记录 3. 查看日志输出13.3、 二级缓存
- 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。
- 基于namespace级别的缓存,一个名称空间,对应一个二级缓存;
- 工作机制
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是会话关闭了,一级缓存的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;
步骤:
1. 开启全局缓存



