Mybatis
MyBatis 框架
第一章 框架的概述
1.三层架构
mvc:web开发中,使用mvc架构模式。 m:数据, v:视图, c:控制器。
c控制器: 接收请求,调用service对象,显示请求的处理结果。 当前使用servlet作为控制器
v视图: 现在使用jsp, html,css,js。 显示请求的处理结果,把m中数据显示出来。
m数据: 来自数据库mysql, 来自文件,来自网络
mvc作用:
1)实现解耦合。
2)让mvc 各负其职。
3)使的系统扩展更好。更容易维护。
三层架构:
1.界面层(视图层):接收用户的请求,调用service, 显示请求的处理结果的。 包含了jsp,html,servlet等对象。 对应的包controller,
2.业务逻辑层:处理业务逻辑, 使用算法处理数据的。 把数据返回给界面层。 对应的是service包,和包中的很多的XXXService类。 例如: StudentService , OrderService, ShopService
3.持久层(数据库访问层):访问数据库,或者读取文件,访问网络。获取数据。 对应的包是dao。 dao包中很多的StudentDao, OrderDao, ShopDao等等。
2. 三层架构请求的处理流程
用户发起请求---->界面层----->业务逻辑层---->持久层---->数据库(mysql)
3. 为什么要使用三层?
1,结构清晰、耦合度低, 各层分工明确
2,可维护性高,可扩展性高
3,有利于标准化
4,开发人员可以只关注整个结构中的其中某一层的功能实现
5,有利于各层逻辑的复用
4. 三层架构模式和框架
每一层对应着一个框架
1)界面层---SpringMVC框架
2)业务层---Spring框架
3)持久层---MyBatis框架
5 .框架
- 什么是框架(framework)
框架:就是一个软件, 完成了部分的功能。 软件中的类和类之间的方法调用都已经规定好了。 通过这些可以完成某些功能。 框架看做是模版。
框架是可以升级的,改造的。 框架是安全的。
框架是对某一个方面有用的,不是全能的。
6. 框架解决的问题
1)框架能实现技术的整合。
2)提供开发的效率。 降低难度。
7. jdbc访问数据库的优缺点
优点:
- 直观,好理解
缺点:
- 创建很多对象 Connection ,Statement, ResultSet
- 注册驱动
- 执行sql语句
- 把ResultSet转为 Student , List集合。
- 关闭资源
- sql语句和业务逻辑代码混在一起
8 MyBatis框架
什么 mybatis: 是一个持久层框架, 原名是ibatis, 2013改名为 MyBatis. MyBatis可以操作数据库,对数据执行增删改查。 看做是高级的jdbc。 解决jdbc的缺点。
mybatis能做什么?
1) 注册驱动 。
2) 创建jdbc中使用的Connection, Statement,ResultSet
-
执行sql语句, 得到ResultSet
-
处理ResultSet, 把记录集中的数据转为java对象, 同时还能把java对象放入到List集合。
5)关闭资源
6)实现sql语句和java代码的解耦合。
mybatis的文档: https://mybatis.org/mybatis-3/zh/index.html
第二章 MyBatis入门
2.1 第一个例子
实现步骤:
创建student表(id,name,email,age)
CREATE TABLE `student` (
`id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
新建maven项目
修改pom.xml
- 加入依赖 mybatis依赖, mysql驱动, junit
- 在
加入资源插件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
4.0.0
com.bjpowernode
ch02-dao
1.0
UTF-8
1.8
1.8
org.mybatis
mybatis
3.5.1
mysql
mysql-connector-java
5.1.9
junit
junit
4.11
test
src/main/java
**/*.properties
**/*.xml
false
创建实体类Student,定义属性, 属性名和列名保持一致
Student.java
package com.dy.domain;
public class Student {
//属性名和列名一样
private Integer id;
private String name;
private String email;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "学生实体的信息{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
创建Dao接口, 定义操作数据库的方法
StudentDao.java
package com.dy.dao;
import com.dy.domain.Student;
public interface StudentDao {
//查询一个学生
Student selectStudentById(Integer id);
//添加学生
//返回值int:表示本次操作影响的数据库的行数
int insertStudent(Student student);
}
创建xml文件(mapper文件), 写sql语句
StudentDao.xml
mybatis框架推荐是把sql语句和java代码分开
mapper文件:定义和dao接口在同一目录, 一个表一个mapper文件。
<?xml version="1.0" encoding="UTF-8" ?>
insert into student values(#{id},#{name},#{email},#{age});
创建mybatis的主配置文件(xml文件):有一个, 放在resources目录下
mybatis.xml
1)定义创建连接实例的数据源(DataSource)对象
- 指定其他mapper文件的位置
<?xml version="1.0" encoding="UTF-8" ?>
创建测试的内容
使用main方法,测试mybatis访问数据库
也可以使用junit 访问数据库
MyTest.java
package com.dy.mybatistest;
import com.dy.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;
public class MyTest {
//测试mybatis执行sql语句
public static void main(String[] args) throws IOException {
//调用mybatis某个对象的方法,执行mapper文件中的sql语句
//mybatis核心类:SqlSessionFactory
//1.定义mybatis主配置文件的位置,从类路径开始的相对路径
// String config="mybatis.xml";
// 2.读取主配置文件。使用mybatis框架中的Resources类
// InputStream inputStream = Resources.getResourceAsStream(config);
// 3.创建SqlSessionFactory对象,使用SqlSessionFactoryBuilder类
// SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//
// 4.获取SqlSession对象
// SqlSession session = factory.openSession();
//
// 5.指定要执行的sql语句的id
// sql的id = namespace + "." + select|update|insert|delete 标签的id属性值
// String sqlId = "com.dy.dao.StudentDao" + "." + "selectStudentById";
// 也可以
// String sqlId = "com.dy.dao.StudentDao.selectStudentById";
// 6.通过SqlSession的方法,执行sql语句
// Student student = session.selectOne(sqlId,"1001");
// System.out.println("使用mybatis查询一个学生:" + student);
//
// 7.关闭SqlSession对象
// session.close();
// insert操作
//1.定义mybatis主配置文件的位置,从类路径开始的相对路径
String config="mybatis.xml";
//2.读取主配置文件。使用mybatis框架中的Resources类
InputStream inputStream = Resources.getResourceAsStream(config);
//3.创建SqlSessionFactory对象,使用SqlSessionFactoryBuilder类
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
//4.获取SqlSession对象
SqlSession session = factory.openSession();
//5.指定要执行的sql语句的id
//sql的id = namespace + "." + select|update|insert|delete 标签的id属性值
String sqlId = "com.dy.dao.StudentDao" + "." + "insertStudent";
//也可以
// String sqlId = "com.dy.dao.StudentDao.selectStudentById";
//6.通过SqlSession的方法,执行sql语句
// int rows = session.insert(sqlId);
Student student = new Student();
student.setId(1004);
student.setName("dg");
student.setEmail("dgsad@");
student.setAge(2);
int rows = session.insert(sqlId,student);
System.out.println("使用mybatis添加一个学生:rows=" + rows);
//mybatis默认执行sql语句是,手工提交事务 模式,在做insert、update、delete后需要提交事务
session.commit();
//7.关闭SqlSession对象
session.close();
}
}
2.2 概念
1.自动提交:当你的 sql语句执行完毕后, 提交事务。 数据库更新操作之间保存到数据
2.手动(手工)提交事务:在你需要提交事务的位置, 执行方法,提交事务或者回顾事务。
2.3 MyBatis的一些重要对象
1) Resources : mybatis框架中的对象, 一个作用读取 主配置信息。
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
2)SqlSessionFactoryBuilder:负责创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
3)SqlSessionFactory: 重要对象
SqlSessionFactory是重量级对象:创建此对象需要使用更多的资源和时间。 在项目中有一个就可以了。
SqlSessionFactory接口:作用是SqlSession的工厂, 就是创建SqlSession对象。
DefaultSqlSessionFactory实现类
public class DefaultSqlSessionFactory implements SqlSessionFactory { }
SqlSessionFactory接口中的方法
openSession(): 获取一个默认的SqlSession对象, 默认是需要手工提交事务的。
openSession(boolean): boolean参数表示是否自动提交事务。
true: 创建一个自动提交事务的SqlSession
false: 等同于没有参数的openSession
- SqlSession对象
SqlSession对象是通过SqlSessionFactory获取的。 SqlSession本身是接口
DefaultSqlSession: 实现类
public class DefaultSqlSession implements SqlSession { }
SqlSession作用是提供了大量的执行sql语句的方法:
selectOne:执行sql语句,最多得到一行记录,多余1行是错误。
selectList:执行sql语句,返回多行数据
selectMap:执行sql语句的,得到一个Map结果
insert:执行insert语句
update:执行update语句
delete:执行delete语句
commit:提交事务
rollback:回顾事务
注意SqlSession对象不是线程安全的, 使用的步骤:
①:在方法的内部,执行sql语句之前,先获取SqlSession对象
②:调用SqlSession的方法,执行sql语句
③:关闭SqlSession对象,执行SqlSession.close()
2.4 使用模版
创建模版,mapper文件模版和mybatis主配置文件模版
创建模版的步骤:
创建模版文件:
创建文件选择使用的模版:
2.5 使用工具类
MyBatisUtil
package com.dy.utils;
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;
//工具类:创建SqlSession对象
public class MyBatisUtil {
private static SqlSessionFactory factory = null;
static {
String config="mybatis.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(config);
factory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (IOException e) {
e.printStackTrace();
}
}
//创建方法,获取SqlSession对象
public static SqlSession getSqlSession() {
SqlSession session = null;
if (factory != null) {
session = factory.openSession(); //openSession(true);
}
return session;
}
}
StudentDao
package com.dy.dao;
import com.dy.domain.Student;
import java.util.List;
public interface StudentDao {
//查询一个学生
Student selectStudentById(Integer id);
//查询多个学生
List selectStudents();
//添加学生
//返回值int:表示本次操作影响的数据库的行数
int insertStudent(Student student);
}
StudentDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
insert into student values(#{id},#{name},#{email},#{age})
测试类
package com.dy.mybatistest;
import com.dy.domain.Student;
import com.dy.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.指定sqlId
String sqlId = "com.dy.dao.StudentDao.selectStudentById";
//3.执行SqlSession的方法,表示执行sql语句
Student student = session.selectOne(sqlId,1001);
System.out.println("查询结果:" + student);
//关闭SqlSession对象
session.close();
}
@Test
public void testSelectStudents() {
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.指定sqlId
String sqlId = "com.dy.dao.StudentDao.selectStudents";
//3.执行SqlSession的方法,表示执行sql语句
List students = session.selectList(sqlId);
for (Student stu:students) {
System.out.println(stu);
}
//关闭SqlSession对象
session.close();
}
@Test
public void testInsertStudents() {
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.指定sqlId
String sqlId = "com.dy.dao.StudentDao.insertStudent";
//3.执行SqlSession的方法,表示执行sql语句
Student student = new Student();
student.setId(1008);
student.setName("du8");
student.setEmail("435dfg");
student.setAge(10);
int rows = session.insert(sqlId, student);
session.commit();
System.out.println(rows);
//关闭SqlSession对象
session.close();
}
}
实现Dao接口的方式
StudentDaoImpl
package com.dy.impl;
import com.dy.dao.StudentDao;
import com.dy.domain.Student;
import com.dy.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class StudentDaoImpl implements StudentDao {
@Override
public Student selectStudentById(Integer id) {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
String sqlId = "com.dy.dao.StudentDao.selectStudentById";
Student student = sqlSession.selectOne(sqlId,id);
sqlSession.close();
return student;
}
@Override
public List selectStudents() {
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.指定sqlId
String sqlId = "com.dy.dao.StudentDao.selectStudents";
//3.执行SqlSession的方法,表示执行sql语句
List students = session.selectList(sqlId);
//关闭SqlSession对象
session.close();
return students;
}
@Override
public int insertStudent(Student student) {
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.指定sqlId
String sqlId = "com.dy.dao.StudentDao.insertStudent";
//3.执行SqlSession的方法,表示执行sql语句
int rows = session.insert(sqlId, student);
session.commit();
System.out.println(rows);
//关闭SqlSession对象
session.close();
return rows;
}
}
测试类
package com.dy.mybatistest;
import com.dy.dao.StudentDao;
import com.dy.domain.Student;
import com.dy.impl.StudentDaoImpl;
import org.junit.jupiter.api.Test;
import java.util.List;
public class MyTest2 {
/*
测试方法中:调用dao的方法
Student student = dao.selectById(1002);
1) dao:通过反射能够得到 全限定类型名称
dao是StudentDao 类型的,全限定名称 com.dy.dao.StudentDao
2) selectById:dao中的方法名称,方法名称就是mapper文件中标签的id
通过dao.selectById() 能得到 sqlId="com.dy.dao.StudentDao.selectById";
3) 确定调用SqlSession的那个方法
1.根据dao接口的方法返回中,如果返回时一个对象,例如Student,调用SqlSession。selectOne();
如果dao接口中的方法返回List,调用 SqlSession 的selectList();
2.根据mapper文件中的标签,如果标签是,调用SqlSession.insert()方法
mybatis框架,发现使用dao的方法调用能确定 执行sql语句的必要信息,mybatis简化dao对象的实现。
由mybatis框架在程序执行期间,根据你的Dao接口,创建一个内存中的接口的实现类对象
mybatis把这个技术叫做dao技术(动态代理,dao的动态代理)
dao代理技术:由mybatis创建StudentDao接口的实现类Proxy(StudentDaoImpl)
使用框架创建的StudentDaoImpl 代替 "你自己手工实现的StudentDaoImpl类的功能",
不用开发人员写 dao 接口的实现类
使用dao的代理要求:
1.mapper 文件中的namespace:必须是dao接口的全限定名称
2.mapper 文件中标签的id 是dao接口中的方法名称(一模一样的)
*/
@Test
public void testSelectOne(){
//要使用dao的方法
//接口类型 变量 = new 接口的实现类();
StudentDao dao = new StudentDaoImpl();
Student student = dao.selectStudentById(1001);
System.out.println(student);
}
@Test
public void testSelectStudents(){
StudentDao dao = new StudentDaoImpl();
List students = dao.selectStudents();
students.forEach(stu -> System.out.println(stu));
}
@Test
public void testInsert(){
StudentDao dao = new StudentDaoImpl();
Student student = new Student();
student.setId(1009);
student.setName("dsf");
student.setEmail("dg4");
student.setAge(10);
dao.insertStudent(student);
}
}
第三章 MyBatis的Dao代理
3.1 dao代理
3.1.1 mybatis提供代理:
mybatis创建Dao接口的实现类对象, 完成对sql语句的执行。 mybatis创建一个对象代替你的 dao实现类功能。
3.1.2 使用mybatis代理要求
1)mapper文件中的namespace 一定dao接口的全限定名称
2)mapper文件中 标签的id是dao接口方法名称
3.1.3 mybatis代理实现方式
使用SqlSession对象的方法 getMapper(dao.class)
例如: 现在有 StudentDao接口。
SqlSession session = MyBatisUtils.getSqlSession();
StudentDao dao = session.getMapper(StudentDao.class);
Student student = dao.selectById(1001);
//上面代码中
StudentDao dao = session.getMapper(StudentDao.class);
等同于
StudentDao dao = new StudentDaoImpl();
所以2.5中的测试方法可以写成
package com.dy.mybatistest;
import com.dy.dao.StudentDao;
import com.dy.domain.Student;
import com.dy.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Test;
import java.util.List;
public class MyTest3 {
@Test
public void testSelectOne(){
//1,获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.获取dao的代理
StudentDao dao = session.getMapper(StudentDao.class);
Student student = dao.selectStudentById(1001);
System.out.println("studnet = " + student);
//3.关闭SqlSession对象
session.close();
}
@Test
public void testSelectStudents(){
//1,获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.获取dao的代理
StudentDao dao = session.getMapper(StudentDao.class);
System.out.println("dao===" + dao.getClass().getName());//com.sun.proxy.$Proxy
List students = dao.selectStudents();
students.forEach(stu -> System.out.println("stu=" + stu));
//3.关闭SqlSession对象
session.close();
}
}
3.2 理解参数
理解参数是: 通过java程序把数据传入到mapper文件中的sql语句。 参数主要是指dao接口方法的形参
3.2.1 parameterType
parameterType:表示参数的类型, 指定dao方法的形参数据类型。 这个形参的数据类型是给mybatis使用。 mybatis在给sql语句的参数赋值时使用。 PreparedStatement.setXXX( 位置, 值)
第一个用法: java类型的全限定类型名称 parameterType="java.lang.Integer"
第二个用法: mybatis定义的java类型的别名 parameterType="int"
parameterType:mybatis通过反射机制可以获取 dao接口方法参数的类型, 可以不写
3.2.2 dao接口方法是一个简单类型的参数
public interface StudentDao {
//重载方法
Student selectById(Integer id);
//dao接口的方法形参是一个简单类型的
//简单类型:java基本数据类型和String
Student selectByEmail(String email);
}
3.2.3 dao接口方法有多个简单类型的参数
@Param: 命名参数, 在方法的形参前面使用的, 定义参数名。 这个名称可以用在mapper文件中。
dao接口,方法的定义
import com.dy.domain.Student;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface StudentDao {
//重载方法
Student selectById(Integer id);
//dao接口的方法形参是一个简单类型的
//简单类型:java基本数据类型和String
Student selectByEmail(String email);
/*
多个简单类型的参数
使用@Param命名参数, 注解是mybatis提供的
位置:在形参定义的前面
属性:value 自定义的参数名称
*/
List selectByNameOrAge(@Param("myname") String name,
@Param("myage") Integer age);
}
mapper文件
3.2.4 dao接口方法使用一个对象作为参数
方法的形参是一个java对象。这个java对象表示多个参数。使用对象的属性值作为参数使用
java对象
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
//set|get方法
...省略
}
public class QueryParam {
private Object p1;
private Object p2;
//set|get方法
...省略
}
dao接口中的方法定义
/*
* 一个java对象作为参数( 对象由属性, 每个属性有set,get方法)
*/
List selectByObject(Student student);
List selectByQueryParam(QueryParam param);
mapper文件
3.2.5 dao接口中多个简单类型的参数,使用位置
参数位置: dao接口中方法的形参列表,从左往右,参数位置是 0 , 1, 2......
语法格式:#{arg0} ,#{arg1}
dao接口的方法
/*
使用位置,获取参数
*/
List selectByPosition(String name,Integer age);
3.2.6 dao接口参数是一个Map
map作为dao接口的参数, 使用 key 获取参数值,mapper文件中,语法格式 #{key}
/*
使用Map作为参数
*/
List selectStudentByMap(Map map);
mapper文件
测试,调用方法的位置
@Test
public void testSelectByMap(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
StudentDao dao = sqlSession.getMapper(StudentDao.class);
//使用map传递参数
Map data = new HashMap<>();
data.put("myname", "李思思");
data.put("myage", 20);
List students = dao.selectStudentByMap(data);
students.forEach( stu-> System.out.println("stu="+stu));
sqlSession.close();
}
3.3 #和$的区别
3.3.1 # 占位符
语法: #{字符}
mybatis处理#{} 使用jdbc对象是 PrepareStatment对象
mybatis出创建PrepareStatement对象,执行sql语句
String sql=" select id,name,email,age from student where id=?";
PrepareStatement pst = conn.prepareStatement(sql);
pst.setInt(1,1001); //传递参数
ResultSet rs = pst.executeQuery(); //执行sql语句
{}特点:
1)使用的PrepareStatement对象,执行sql语句,效率高。
2)使用的PrepareStatement对象,能避免sql注入, sql语句执行更安全。
3) #{} 常常作为 列值使用的, 位于等号的右侧, #{}位置的值和数据类型有关的。
3.3.2 $ 占位符
语法 : ${字符}
mybatis执行${}占位符的sql语句
${} 表示字符串连接, 把sql语句的其他内容和 ${}内容使用 字符串(+) 连接的方式连在一起
String sql="select id,name,email,age from student where id=" + "1001";
mybatis创建Statement对象, 执行sql语句。
Statement stmt = conn.createStatement(sql);
ResultSet rs = stmt.executeQuery();
${} 的特点
1)使用Statement对象,执行sql语句,效率低
2)${}占位符的值,使用的字符串连接方式, 有sql注入的风险。 有代码安全的问题
- ${} 数据是原样使用的, 不会区分数据类型。
4)${} 常用作 表名或者列名, 在能保证数据安全的情况下使用 ${}
3.4 封装MyBatis输出结果
封装输出结果: MyBatis执行sql语句,得到ResultSet, 转为java对象。
讲两个 resultType, resultMap
3.4.1 resultType
resultType属性: 在执行select时使用, 作为