目录
mybatis简介
mybatis是什么?
mybatis搭建1.0
1.创建一个maven项目,添加mybatis,mysql依赖的jar
2.创建本地的git仓库
3.创建一个数据库表,以及一个应的java模型类
4.创建mybatis全局配置文件,配置数据库连接信息编辑
5.创建sql映射文件 定义一个与接口方法名相同的查询语句,配置sql映射文件
6.创建一个访问接口 定义一个方法
7.测试mybatis
mybatis搭建2.0
1.环境
2.事务管理
3.配置数据源
pool数据库连接池(缓冲池)
数据库连接池思想
4.1创建并导入配置属性文件
4.2调整设置
4.3别名
5.TestAdmin 解释每一操作步骤的含义
6.进行一个简单的封装处理
API说明
mybatis测试
增删改
添加
修改
删除
结果处理
一般查询结果
无条件查询结果
对象映射
自动映射
手动映射
多表关联处理
准备工作
关联查询
嵌套查询
注解方式
mybatis的动态SQL
if
where,set
trim
choose,when
foreach
补充:
mybatis的一级缓存和二级缓存
引入
mybatis一级缓存
【前言】本文只作为作者的学习记录,如果能帮到各位技术萌新,是我的荣幸,如果有专业失误,也烦请大佬指出。
原来是Apache的一个 开源项目iBatis,2010年转投谷歌,从ibatis3.0开始更名为MyBatis。
是一个优秀的数据持久层(dao)
这里牵扯到java后端的一个三层架构
servlet(web) 接受请求,调用其他java程序处理,响应
service 业务逻辑层
dao 数据访问/持久
mybatis是对jdbc进行一个轻量级封装
提供一些自己定义的类和接口来实现功能
提供专门xml文件来进行配置,以及可以自动的对查询结果进行封装
是一个ORM(java对象与数据库映射)实现的数据持久层的框架
支持动态sql,以及数据缓存
这里附上Mybatis中文网站
前三步可视为准备工作
下载到本地仓库(更新)
创建过滤文件(忽略)
接口代码:
import com.ffyc.mybatis.model.Admin;
public interface AdminDao {
public Admin FindAdminByid(int id);
}
测试类代码:mybatis搭建(第一代)
import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
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.Reader;
public class TestAdmin {
public static void main(String[] args) throws IOException {
//1.读取配置文件
Reader reader = Resources.getResourceAsReader("mybatis.xml");
//2.创建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
//3.创建 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
//4.获得接口代理对象
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
//5.使用这个对象去调用对应方法
Admin admin = adminDao.FindAdminByid(1);
System.out.println(admin);
//6.关闭SqlSession
sqlSession.close();
}
}
指定环境俩个实现方式
1.创建SqlSession时指定
代码:
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader,"development");
2.配置环境是指定为默认环境
代码:
数据库事务
是对一次数据库操作过程中的多条执行sql进行管理控制
保证一次执行中的多条sql能够作为一个整体,要么都执行成功,要么都执行失败。
现在每与数据库交互一次,创建一次数据库连接对象(Connection,SqlSession),用完就会关闭销毁
下一次需要,重复出现这过程,
问题:频繁创建销毁对象,需要开销(内存,效率)
池的概念:可以理解为一个集合
可以在启动时设置一个容器,在里面初始化好一些数据库连接对象
有请求到来时,可以不用每次都创建销毁,可以重复使用。
减少了频繁创建销毁连接对象的开销
一般的设置
初始连接对象的数量
最大连接对象的数量
最大等待时间
创建属性文件,多组键值对
配置属性文件
代码:
1.
//读取mybatis核心配置文件
Reader reader = Resources.getResourceAsReader("mybatis.xml");
2.
//创建SqlSessionFactory
//SqlSessionFactory封装了所有的配置信息,
//SqlSessionFactory负责生成一个数据库连接的会话对象SqlSession对象
//SqlSessionFactory创建开销比较大,所以在整个项目中只需要创建一次即可,不用销毁
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
3.
//创建SqlSession session会话,一次与数据库交互,类似于之前使用的Connection
//SqlSessionFactory中的openSession()方法用来创建一个SqlSession对象,默认无参的默认设置事物提交为false(手动提交)
SqlSession sqlSession = sessionFactory.openSession();
4.
//创建访问接口的代理对象
//现在mybatis建议使用接口化访问, 先定义一个接口(规范),把每个接口与sql映射文件进行绑定
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
5.
//使用代理对象访问接口中对应的方法,本质是调用的是接口对应的sql映射文件中的那个sql
Admin admin = adminDao.findAdminById(2);
6.
//关闭与数据库连接会话对象
//
sqlSession.close();
将读取配置文件,创建SqlSessionFactory以及创建一个SqlSession对象封装成一个类
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.Reader;
public class MybatisUtil {
static SqlSessionFactory sessionFactory = null;
static{
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis.xml");
} catch (IOException e) {
e.printStackTrace();
}
sessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
public static SqlSession getSqlSession(){
return sessionFactory.openSession();
}
}
测试
#{}与${}的区别
#{参数名}: 首先是采用预编译的方式传值,一般用于向sql中传值使用,更加的安全
${参数名}: '${参数名}' 使用字符串拼接方式传值,不安全, 一般可以用于动态向sql中传列名
举个例子:
购物网站的排序,需要直接读取到属性名时,直接使用'${属性名}'就行。
代码:
insert into admins(account,password) value (#{account},#{password})
update admins set account=#{account},password=#{password} where id = #{id}
delete from admins where id = #{id}
代码:
import com.ffyc.mybatis.model.Admin;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface AdminDao {
public Admin findAdminByid(int id);
//public void saveAdmin1(@Param("acc") String account, @Param("pwd") String password);
//void saveAdmin(Admin admin);
void insertAdmin(Admin admin);
void updateAdmin(Admin admin);
void deleteAdmin(int id);
//List findAdmin();
//List findAdmins();//resultMapdemo
}
Test代码:
import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.io.IOException;
public class TestAdmin3 {
public static void main(String[] args) throws IOException {
//增加
SqlSession sqlSession = MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
//String account = "tom";
//String password = "123";
//adminDao.saveAdmin1(account,password);
String account = "jack";
String password = "222";
Admin admin = new Admin();
admin.setAccount(account);
admin.setPassword(password);
adminDao.insertAdmin(admin);
sqlSession.commit();
System.out.println(admin.getId());
sqlSession.close();
}
}
import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.io.IOException;
public class TestAdmin3 {
public static void main(String[] args) throws IOException {
//修改
SqlSession sqlSession = MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
String account = "JACK";
String password = "000";
Admin admin = new Admin();
admin.setId(4);
admin.setAccount(account);
admin.setPassword(password);
adminDao.updateAdmin(admin);
sqlSession.commit();
sqlSession.close();
}
}
import com.ffyc.mybatis.dao.AdminDao;
import com.ffyc.mybatis.model.Admin;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.io.IOException;
public class TestAdmin3 {
public static void main(String[] args) throws IOException {
//删除
SqlSession sqlSession = MybatisUtil.getSqlSession();
AdminDao adminDao = sqlSession.getMapper(AdminDao.class);
adminDao.deleteAdmin(3);
sqlSession.commit();
sqlSession.close();
}
}
返回值一般是简单类型
一般情况下会查询到多条数据,这是就需要集合来接收。
List findAdmin();
当数据库列名与java模型中的属性不一致时,该属性值无法被封装到对象中。
解决办法:
1.将不匹配的列名as成java模型的属性名,就可以正常封装到对象中。
2.Mybatis中的对象映射同样可以解决这样的问题,当然前提是,数据库中使用标准的下划线格式像user_name,并且java模型类中使用标准的驼峰命名格式的时候,像userName。在mybatis.xml中设置自动映射即可。
代码:
编程小tip:类名的首字母要大写,目的就是为了与创建的对象区分,还有创建类之后,如果生成了有参的构造方法,要将无参的构造方法写出来,方便后续使用,养成良好的变成习惯。
也就是特殊定义处理
1. resutlMap 的 id 属性是 resutlMap 的唯一标识
2.resutlMap 的 id 属性是映射的 POJO 类
3.id 标签映射主键,result 标签映射非主键
4.property 设置 POJO 的属性名称,column 映射查询结果的列名称
代码:
1.创建表
2.创建模型类
3.创建接口,定义抽象方法
代码:
可以看做是多个单表查询,将一个多表关联查询拆分为多次查询,先查询主表数据,然后查询关联表数据。
代码:
查询宿舍
1.一个宿舍中有多个学生
代码:
测试代码:
import com.ffyc.mybatis.dao.DormDao;
import com.ffyc.mybatis.model.Dorm;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.Date;
import java.util.List;
public class TestDorm {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
DormDao dormDao = sqlSession.getMapper(DormDao.class);
Dorm dorm = dormDao.findDorm(1);
System.out.println(dorm);
//List list = dormDao.findAllDorm();
//List list = dormDao.findAllDorm1();
//System.out.println(list);
/*
for(Dorm dorm:list){
System.out.print(dorm.getNum());
for(Student student: dorm.getList()){
System.out.println(student.getName());
}
}
*/
sqlSession.close();
}
}
2.查询所有的宿舍
嵌套查询
配置映射:
代码:
Test测试
测试代码:
import com.ffyc.mybatis.dao.DormDao;
import com.ffyc.mybatis.model.Dorm;
import com.ffyc.mybatis.model.Student;
import com.ffyc.mybatis.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.Date;
import java.util.List;
public class TestDorm {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
DormDao dormDao = sqlSession.getMapper(DormDao.class);
//Dorm dorm = dormDao.findDorm(1);
//System.out.println(dorm);
//List list = dormDao.findAllDorm();
List list = dormDao.findAllDorm1();
System.out.println(list);
for(Dorm dorm:list){
System.out.print(dorm.getNum());
for(Student student: dorm.getList()){
System.out.println(student.getName());
}
}
sqlSession.close();
}
}
测试结果
通过日志可以很清楚的看到宿舍对应学生的关系
结果:
[Dorm{id=1, num=101, list=[Student{id=1, num=1001, name='张三', gender='男', dorm=null, admin=null}, Student{id=3, num=1003, name='王五', gender='男', dorm=null, admin=null}, Student{id=5, num=1005, name='jim', gender='男', dorm=null, admin=null}]}, Dorm{id=2, num=102, list=[Student{id=2, num=1002, name='李四', gender='女', dorm=null, admin=null}]}, Dorm{id=3, num=103, list=[Student{id=4, num=1004, name='赵六', gender='男', dorm=null, admin=null}, Student{id=6, num=1006, name='tom', gender='男', dorm=null, admin=null}]}]
所有宿舍封装到一个结果中,每一个宿舍中又封装了一个学生的集合,集合中包括多个学生。
101张三
王五
jim
102李四
103赵六
tom
代码:
import com.ffyc.mybatis.model.Student;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Update;
import java.util.List;
public interface StudentDao_back {
Student findStudentByid(int id);
Student findStudentByid1(int id);
List findAllStudent();
@Delete("delete from student where id = #{id}")
void deleteStudentByid(int id);
@Insert("insert into student(id,num,name,gender) value(#{id},#{num},#{name},#{gender})")
void addStudent(Student student);
@Update("update student set gender=#{gender} where id = #{id}")
void updateStudent(int id,String gender);
}
动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。
使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
没有动态Sql之前这里会出现一个非常令人头疼的问题。
但是如果没有其中的某一个条件呢?就不会查到任何记录,if元素就可以解决这一问题。使用if元素进行判断一下。但是又出现了新的问题。
什么问题呢?请先看下面这段代码。
问题代码:
我们要筛选数据时,通常对属性加以限制,但是又不是全部都加,当对num加以限制时,默认num!=0,这时可以正常执行sql语句,但是如果num==0时,sql语句变为
select num,name,gender from student where and name = #{name} and gender = #{gender}
这时可以很清楚的看到这条sql语句是有问题的,系统就会报错,这是就出现这样一种写法,这个问题就迎刃而解了。
代码:
就是这一个小小的1=1,解决这个大大的问题。那么mybatis又是怎么解决的呢?
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。这样就解决了这一问题。
mybatis中的动态sql就可以很好的解决这一问题。
先来看代码:
-->
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
update student
num = #{num},
name = #{name},
gender = #{gender}
where id=#{id}
mybatis还对此做了优化,也就是trim元素
trim元素可以代替where元素,也就是把where设置成前缀,当 WHERE 后紧随 AND 或则 OR 的 时候,就去除 AND 或者 OR。prefix 前缀,prefixOverrides 覆盖首部指定内容。
当然前缀也可以设置成set,比如
update student
num = #{num},
name = #{name},
gender = #{gender}
where id=#{id}
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:删除id在某个区间的多个学生信息。
delete from student
#{item}
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
比如说:要删除id小于某一个值的学生信息时,正常书写 mybatis 会报错,需要对这些符号进行转义。这里的"<"与就是标签的左半部分,又能表示小于的意思,就会出错,当然有问题,肯定就有解决办法。
方法一:
特殊字符 | 转义字符 |
---|---|
< | < |
> | > |
" | " |
, | ' |
& | & |
遇到上述特殊字符时,使用对应的转义字符即可。
方法二:
首先,什么是缓存?
缓存用来存储一些常用或即将用到的数据或指令,当CPU需要这些数据或指令的时候直接从缓存中读取,这样比CPU到内存甚至硬盘中读取要快得多,能够大幅度提升CPU的处理速度(节省时间/提高效率)。
缓存有什么用处?
为让程序更快的访问到数据,同时也是为了减少数据库的访问压力,可以将数据缓存到内存,手机内存,客户端硬盘中。
举个例子:同时有100个用户访问同一个数据库,容易造成数据库崩溃,但如果将第一个用户访问到的数据加入到缓存中,后面的用户直接访问第一个用户访问拿到的数据,之后定时再从数据库中查询以更新缓存,这样就能缓解数据库访问量过大的问题,减少数据库压力,提高查询效率。
缓存是怎么实现的?
Mybatis 有一级缓存和二级缓存,一级缓存的作用域是同一个 SqlSession,如果在同一个SqlSession中执行两次一样的sql,那么第一次执行完毕后会将在数据库中查到的数据写入缓存,第二次查询时就会直接从缓存中查询,提高查询效率,当一个 sqlSession 结束后该 sqlSession 中的一级缓存也就不存在了。Mybatis 默认开启一级缓存。
,一级缓存只是相对于同一个 SqlSession 而言,如果没有声明需要刷新,并且缓存没有超时的情况下,SqlSession 都会取出当前缓存的数据,而不会再次发送 SQL 到数据库。
生命周期
@Test
public void test3(){
//测试一级缓存
//结果只查询1次 没有设置缓存更新,也没有中间强出缓存
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.findStudents(13);
studentDao.findStudents(13);
sqlSession.commit();
sqlSession.close();
}
//结果只查询1次 没有设置缓存更新,也没有中间强出缓存
@Test
public void test2(){
//测试一级缓存 中间清除一次缓存
//结果只查询2次
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.findStudents(13);
sqlSession.clearCache();
studentDao.findStudents(13);
sqlSession.commit();
sqlSession.close();
}
//测试一级缓存 中间清除一次缓存
//结果只查询2次
@Test
public void test(){
//测试一级缓存
//在select中设置缓存更新
//结果只查询2次
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
studentDao.findStudentts(13);
studentDao.findStudents(13);
sqlSession.commit();
sqlSession.close();
}
//测试一级缓存
//在select中设置缓存更新
//结果只查询2次
mybatis二级缓存
二级缓存是多个SqlSession共享的,是SqlSessionFactory级别的,如果将查询到的数据放到二级缓存中,那么就可以实现多个sqlsession共享。
如果执行两次相同的sql,第一次查询到的数据后关闭sqlsession,就会将数据放到二级缓存中。
也可以理解为根据 mapper 的 namespace 划分区域 的,相同 namespace 的 mapper 查询的数据缓存在同一个区域,如果使用 mapper 代理方法每个 mapper 的 namespace 都不同,此时可以理解为二级缓存区域是根据 mapper 划分。
Mybatis 内部存储缓存使用一个 HashMap,key 为 hashCode+sqlId+Sql 语句。value 为从查询出来映射生成的 java 对象。
二级缓存配置:
1.启用二级缓存
2.对象序列化
基本上就是这样。这个简单语句的效果如下:
提示 缓存只作用于 cache 标签所在的映射文件中的语句。
public void testTwo(){
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
StudentDao studentDao1 = sqlSession1.getMapper(StudentDao.class);
studentDao1.findStudents(10);
sqlSession1.close();
//模拟刷新时间
int j=0;
for(int i=0;i<=1000000;i++){
j=j+1;
}
System.out.println(j);
SqlSession sqlSession2 = MybatisUtil.getSqlSession();
StudentDao studentDao2 = sqlSession2.getMapper(StudentDao.class);
studentDao2.findStudents(10);
sqlSession2.close();
}
这里可以看到结果查询了两次。因为第二次查询过了自动刷新时间。
我们再来看一下两次连续查询的,也就是将模拟间隔时间去掉
这里明显只查询了一次。
总体架构:
最后附上官方解释
mybatis – MyBatis 3 | XML 映射器
【本文可能讲述的不够细致,具体可参考mybatis官网】