- Mybatis
- 1. 框架概述
- 1.1 三层架构
- 1.2 框架概念
- 1.3 Mybatis简介
- 2. 分析JDBC缺点
- 3. 实现步骤
- 3.1 加入依赖
- 3.2 创建数据实体类
- 3.3 创建dao接口
- 3.4 创建sql映射文件
- 3.5 创建jdbc配置文件
- 3.5 创建主配置文件
- 3.6 访问数据库
- 4. 实现类介绍
- 5. 获取session封装
- 6. 动态代理实现类
- 7. 深入理解参数
- 7.1 传入单个简单参数
- 7.2 传入多个简单参数
- 7.2.1 使用注解(重要)
- 7.2.2 封装参数(重要)
- 7.2.3 指定顺序
- 7.2.4 封装map
- 7.3 $和#的区别
- 8. 封装输出结果
- 8.1 resultType
- 8.1.1 简单类型参数
- 8.1.2 对象类型参数
- 8.1.3 Map
- 8.2 resultMap
- 8.3 as重写字段名
- 8.3 定义封装对象别名
- 9. 动态SQL
- 9.1 if
- 9.2 where
- 9.3 foreach
- 9.4 代码复用
- 10. 分页输出
- 10.1 导入依赖项
- 10.2 主配置文件导入插件
- 10.3 实现分页
- 10. 分页输出
- 10.1 导入依赖项
- 10.2 主配置文件导入插件
- 10.3 实现分页
-
mybatis的事务默认是不自动提交的
-
主配置文件添加这个可以显示日志
1. 框架概述 1.1 三层架构
界面层(controller包)
springmvc框架
和用户对接,接受用户的请求参数,显示处理结果。
jsp、html、servlet
业务逻辑层(XXXservice包)
spring框架
接收界面层传递的数据,计算逻辑,调用数据库,获取数据
数据访问层(XXXDao包)
mybatis框架
访问数据库,执行对象数据的查询等操作
框架是一个模板,其中已经定义好了一些功能,这些功能是可用的,可以在项目中加入自己的功能。
框架是一个半成品软件,定义好的基础功能是可重复使用的,可升级的。
框架特点:
- 不是全能,不能做所有事情
- 只是针对某一个领域有效
- 是一个软件
1.3 Mybatis简介
Mybatis是一个sql映射框架,提供数据库的操作能力,增强的JDBC,使用这个只需要写好sql语句即可。
Mybatis是一个框架,早期叫做ibatis
MyBatis SQL Mapper framework for Java (sql映射框架)
-
sql mapper:sql映射
把数据库表中的一行数据映射成一个java对象,一行数据可以看作是一个java对象。操作这个对象,就相当于操作表中的数据
-
Data Access Objects(DAOs):数据访问
对数据库执行增删改查
提供的功能:
- 创建Connection、Statement等能力
- 执行sql语句
- 循环sql,将结果转为java对象,List集合
- 关闭资源
开发人员只需提供sql语句
重复代码太多,开发效率降低。
以上过程完全可以使用反射机制代替,而Mybatis框架就是别人提前写好的java代码,这Mybatis框架封装了JDBC代码,Mybatis框架就是别人已经写好的java代码。
sql编写在java程序中。
sql语句可能在后期是需要调优的,语句被修改的概率很高,然而在修改了sql语句导致java代码重新编译,项目重新部署,十分繁琐…
严重违背开闭原则:OCP
3. 实现步骤 3.1 加入依赖
com.microsoft.sqlserver mssql-jdbc 9.2.0.jre15 org.mybatis mybatis 3.4.6
3.2 创建数据实体类
public class Student {
private String sno;
private String sname;
private String ssex;
private Integer sage;
private String sdept;
public String getSno() {
return sno;
}
public void setSno(String sno) {
this.sno = sno;
}
@Override
public String toString() {
return "Student{" +
"sno='" + sno + ''' +
", sname='" + sname + ''' +
", ssex='" + ssex + ''' +
", sage=" + sage +
", sdept='" + sdept + ''' +
'}';
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSsex() {
return ssex;
}
public void setSsex(String ssex) {
this.ssex = ssex;
}
public Integer getSage() {
return sage;
}
public void setSage(Integer sage) {
this.sage = sage;
}
public String getSdept() {
return sdept;
}
public void setSdept(String sdept) {
this.sdept = sdept;
}
}
3.3 创建dao接口
其中定义操作数据库的方法
public interface StudentDao {
// 查询所有数据
List findAll();
}
3.4 创建sql映射文件
3.5 创建jdbc配置文件
在resources下写properties文件
jdbc.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc.url=jdbc:sqlserver://localhost:1433;Database=S_T jdbc.user=sa jdbc.password=xxxxqq76985
3.5 创建主配置文件
必须指定mapper映射文件,因为在写sqlId的时候,会在指定mapper中寻找namespace
3.6 访问数据库
import com.company.domain.Student;
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;
import java.util.List;
// 这是一个测试类
public class Test {
public static void main(String[] args) throws IOException {
// 1. 定义mybatis主配置文件的名称,从类路径开始
String config = "data.xml";
// 2. 读取这个config表示的文件
InputStream is = Resources.getResourceAsStream(config);
// 3. 创建SqlSessionFactoryBuilder对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 4. 创建SqlSessionFactory对象
SqlSessionFactory factory = builder.build(is);
// 5. [重要] 获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 6. [重要] 指定要执行sql语句的标识,sql映射文件中的namespace + "." + 标签的id值
String sqlId = "com.company.dao.StudentDao" + "." + "findAll";
// 7. 执行sql语句
List studentList = sqlSession.selectList(sqlId);
// 8. 输出结果
for (Student student : studentList) {
System.out.println(student);
}
// 9. 关闭SqlSession对象
sqlSession.close();
}
}
String sqlId = “com.company.dao.StudentDao” + “.” + “findAll”;
com.company.dao.StudentDao匹配的是StudentDao的namespace
4. 实现类介绍
-
Resources
mybatis中的一个类,负责读取主配置文件
-
SqlSessionFactoryBuilder
创建SqlSessionFactory对象
-
SqlSessionFactory(接口)
重要对象,创建该对象耗时长,使用资源多,只要一个即可
获取SqlSession对象
openSession说明:
- 无参数,获取到非自动提交事务的SqlSession对象
- true,获取自动提交事务的SqlSession对象
-
SqlSession(接口)
定义了操作数据的方法
SqlSession不是线程安全的,需要在方法内部使用,使用后关闭才能保证线程安全
5. 获取session封装
public class MybatisUtil {
private static SqlSessionFactory factory;
static {
String config = "data.xml";
try {
InputStream is = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(is);
} catch (IOException e) {
e.printStackTrace();
}
}
private MybatisUtil() {}
public static SqlSession getSqlSession() {
return factory.openSession();
}
public static SqlSession getSqlSession(boolean flag) {
return factory.openSession(flag);
}
}
6. 动态代理实现类
Mybatis帮你创建dao接口的实现类,在实现类中调用SqlSession的方法执行sql语句
(反射机制)
实现动态代理的条件:
- dao接口和mapper文件尽量放在一起
- dao接口和mapper文件名称一致
- mapper文件中的namespace是dao接口的全限定名称
- mapper文件中的操作标签id值是接口中的方法名称
- dao接口不要使用重载方法(同名,不同参数)
public class Main {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
// 动态代理获取接口实现类
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List studentList = dao.findAll();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
}
7. 深入理解参数
public interface StudentDao {
// 查询所有数据
List findAll();
// 插入数据
int insert(Student stu);
// 根据Sno删除
int deleteBySno(String sno);
// 根据姓名年龄查询
List findByNameSex (@Param("name") String name, @Param("sex") String sex);
}
动态代理创建的实现类执行方法时,会检测出参数的类型,自动会赋给sql语句中的占位符
但是如果参数类型是类,那么占位符中的名字必须是类中的属性名,动态代理机制会检测出该属性名的getter方法。
#{传入类的属性名}
7.1 传入单个简单参数
这个占位符名字任意(前面是#的前提)
#{任意字符}
${指定名称}
简单参数:
- java中基本数据类型
- String类型
delete from Student where Sno=#{sno}
public void testDelete() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Student stu = new Student();
stu.setSno("201515010");
int num = dao.deleteBySno("201515010");
sqlSession.commit();
System.out.println(num);
// 9. 关闭SqlSession对象
sqlSession.close();
}
7.2 传入多个简单参数
public void testFindByNameSex() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
String name = "张";
String sex = "男";
List studentList = dao.findByNameSex(name, sex);
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
7.2.1 使用注解(重要)
如果方法需要传入多个参数,需要在定义方法参数中使用注解指定在sql映射文件中占位符的名称
ListfindByNameSex (@Param("name") String name, @Param("sex") String sex);
7.2.2 封装参数(重要)
这个方法是将参数封装成对象,类似于sqlSession.insert(String sqlId, Object obj)
在原方法中通过obj的getter方法找到参数,而封装参数就类似于此
public class FindByNameSex {
private String paramName;
private String paramSex;
public String getParamName() {
return paramName;
}
public void setParamName(String paramName) {
this.paramName = paramName;
}
public String getParamSex() {
return paramSex;
}
public void setParamSex(String paramSex) {
this.paramSex = paramSex;
}
}
ListfindByNameSex (FindByNameSex params);
简化写法是:#{属性名}
具体写法是:#{属性名, javaType=属性名类型, jdbcType=属性在数据库的类型}
javaType和jdbcType是能在mybatis反射获取
7.2.3 指定顺序
Parameter ‘id’ not found. Available parameters are [arg1, arg0, param1, param2],这句话的意思就是id找不到,可用的参数是[arg1, arg0, param1, param2]。所以可使用参数出现的顺序号码引用参数,第一个参数用arg0或param1表示,第二个参数用arg1或param2表示,以此类推(arg从0开始计数,param从1开始计数)。
7.2.4 封装map
在主函数中将参数封装成map集合,key是映射文件占位符的名字,value就是占位符的值
这种方式不推荐!!!
因为别人看代码十分不方便
ListfindByNameSex (Map params);
public void testFindByNameSex() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Map params = new HashMap<>();
params.put("myName", "张");
params.put("mySex", "男");
List studentList = dao.findByNameSex(params);
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
7.3 $和#的区别
select * from student where id=#{studentId}
select * from student where id=?
select * from student where id=${studentId}
select * from student where id=1001
| #{} | ${} |
|---|---|
| 使用PreparedStatement | 使用Statement |
| 效率高 | 效率低 |
| 大程度避免SQL注入 | 无法避免SQL注入 |
| 可替换表名或列名 |
8. 封装输出结果 8.1 resultType
输出的参数类型最好是全限定名称。
可以是别名
8.1.1 简单类型参数
select count(*) from Student
8.1.2 对象类型参数
注意:
- 封装对象的属性名要和字段名相同(不区分大小写)
- 封装对象一定要有setter和getter方法
封装输出对象
public class SelectAge {
private String sname;
private int sage;
public String getStuName() {
return sname;
}
public void setStuName(String sname) {
this.sname = sname;
}
public int getStuAge() {
return sage;
}
public void setStuAge(int sage) {
this.sage = sage;
}
@Override
public String toString() {
return "SelectAge{" +
"sname='" + sname + ''' +
", sage=" + sage +
'}';
}
}
输出结果设为该对象
执行查找
@Test
public void testSelectAge() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List stuList = dao.selectAge();
for (SelectAge stu : stuList) {
System.out.println(stu);
}
sqlSession.close();
}
8.1.3 Map
返回的对象是Map的话,列名是key,值是value
但是不能返回多个map,除非使用List
8.2 resultMap
resultMap的作用是当封装对象的属性名和列名不一致的时候
在sql映射文件中配置属性名和字段名的映射关系
如果不使用resultMap,封装对象的属性名必须和字段名相同(不区分大小写)
主键字段使用id
其他字段使用result
8.3 as重写字段名
为使属性名和字段名匹配,在不使用resultMap时,可在sql语句中重写字段名使其与属性名一致
8.3 定义封装对象别名
定义返回封装对象别名是在主配置文件中配置
第一种方式:
第二种方式:
name是包名,这个包中的所有类的类名就是别名,此时类名不区分大小写
根据条件,能够得到不同的sql语句,使用mybatis的标签即可实现
9.1 if
部分sql语句
示例:
public void testSelectAgeIf() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Student stu = new Student();
stu.setSname("张力");
stu.setSage(19);
List stuList = dao.selectAgeIf(stu);
for (SelectAge student : stuList) {
System.out.println(student);
}
}
用来包含多个 的
当多个if中有只有一个成立,会自动增加一个where关键字并去除多余的and,or等
当多个if中有多个if成立,会自动删除第一个if中多余的and,or等,其余if中多余字段保留
示例:
@Test
public void testSelectAgeIf() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
Student stu = new Student();
stu.setSname("张力");
stu.setSage(19);
List stuList = dao.selectAgeIf(stu);
for (SelectAge student : stuList) {
System.out.println(student);
}
}
循环java数组,list集合的。主要用于sql的in语句中
相关标签参数:
-
collection
接口中方法参数的类型,数组是array,集合是list
-
item
自定义,表示成员变量
-
open
循环开始时的字符
-
close
循环结束时的字符
-
separator
集合成员之间的分隔符
示例:
@Test
public void testSelectAgeFor() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
List idList = new ArrayList<>();
idList.add("201415010");
idList.add("201415011");
idList.add("201415012");
List stuList = dao.selectAgeFor(idList);
for (SelectAge student : stuList) {
System.out.println(student);
}
}
foreach标签的用法有很多巧妙之处:
结果:
select Sno, Sname, Sage from Student where Sno in ( ? , ? , ? , )
select Sno, Sname, Sage from Student where Sno in( #{id} , -1, )
结果:
select Sno, Sname, Sage from Student where Sno in( ? , ? , ? , -1, )
如果集合或者数组中封装的是对象,需要遍历对象当中固定某个属性,可以使用 #{对象.属性名} 遍历
可以将多次出现的sql语句进行复用
select Sno, Sname, Ssex, Sage, Sdept from Student
where Ssex = '男'
通过一个插件能够将结果分页显示,这个功能不是mybatis提供的。
10.1 导入依赖项
com.github.pagehelper pagehelper 5.2.0
10.2 主配置文件导入插件
...
10.3 实现分页
@Test
public void testFindAll() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
// 实现分页操作
PageHelper.startPage(1,5);
List studentList = dao.findAll();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
这个PageHelper.startPage(1,5);表示第一页的五行数据,如果要查看第二页的五行数据则是PageHelper.startPage(2,5);
可以将多次出现的sql语句进行复用
select Sno, Sname, Ssex, Sage, Sdept from Student
通过一个插件能够将结果分页显示,这个功能不是mybatis提供的。
10.1 导入依赖项
com.github.pagehelper pagehelper 5.2.0
10.2 主配置文件导入插件
...
10.3 实现分页
@Test
public void testFindAll() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
// 实现分页操作
PageHelper.startPage(1,5);
List studentList = dao.findAll();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
这个PageHelper.startPage(1,5);表示第一页的五行数据,如果要查看第二页的五行数据则是PageHelper.startPage(2,5);



